Simplified ZK Event Processing With Java Reflection API

From Documentation
DocumentationSmall Talks2008AugustSimplified ZK Event Processing With Java Reflection API
Simplified ZK Event Processing With Java Reflection API

Author
Qamaralzaman Habeek, Sysnet Software SARL, Algeria
Date
August 25, 2008
Version


Introduction

ZK , the great framework, has lately introduced a new class, GenericForwardComposer, which makes UI code a lot more easier. It simply boils down a programmer's task of listening to events to only writing event$id methods and letting the framework do the dirty boilerplate work, hence, resulting in a far more clearer UI code.

However, having to inherit from GenericForwardComposer turns to be a bit restrictive as this feature is unavailable if one has to inherit from other class for any reason.

Discussed in this article is an alternative way which provides the same feature without requiring the inheritance from any particular class. This is achieved with the help of Java reflection API and a method naming pattern convention.


User Interface Code, The normal way

User Interface (UI) code is usually cluttered by re recurring boilerplate sections which associate a listener with an event and attach the whole thing to a given control in the same way again and again. Thus, wasting a programmer's time with unnecessary copy/paste actions and making the resulting code more difficult to understand. Consider, the following contrived example. The example is kept arbitrarily simple but general to clearly show the idea. It simply sums the values of two integer boxes and shows the result in a third one. The result is shown upon an OK event received on either of the first two controls or on a Click event received on a button. The following code shows how the events are wired to the various controls:

<?xml version="1.0" encoding="UTF-8"?>
<window apply="sysnet.test.TestCtrl2">
    <grid width="400px">
        <columns>
            <column width="150px"/>
            <column width="200px"/>
        </columns>
        <rows>
            <row>
                Value 1 : <intbox id="first" />
            </row>
            <row>
                Value 2 : <intbox id="second" />
            </row>
            <row>
                Result : <intbox id="result" />
            </row>
            <row spans="2">
                <button id="compute" label="Compute"/>
            </row>
        </rows>
    </grid>
</window>


 public void doAfterCompose(final Component comp) throws Exception {
    public void doAfterCompose(final Component comp) throws Exception {
        comp.getFellow("first").addEventListener("onOK", new EventListener() {
            public void onEvent(Event event) throws Exception {
                showResult(comp);
            }
        });
            
        comp.getFellow("second").addEventListener("onOK", new EventListener() {
            public void onEvent(Event event) throws Exception {
                showResult(comp);
            }
        });
       
        comp.getFellow("compute").addEventListener("onClick", new EventListener() {
            public void onEvent(Event event) throws Exception {
                showResult(comp);
            }
        });
    }

    private void showResult(Component comp) {
        ((Intbox) comp.getFellow("result")).setValue(
                ((Intbox) comp.getFellow("first")).getValue() +
                ((Intbox) comp.getFellow("second")).getValue());
    }
}

88.89% of this code, 40 out of 45 lines, is repetitive boilerplate code, while the business logic is only 11.11%, about 5 out of 45 lines. The initial programmer has every time to write that amount of boilerplate code again and again, and a subsequent maintainer has to read that much amount of code in order to find the small deeply buried business logic section. So, let's look for a new way to make life easier.


User Interface Code, The Java Reflection API way

Here is the new code re-written with the help of a small helper class employing Java reflection API and a little coding convention to write a listener method as eventName$controlId.

public class TestCtrl2 implements Composer {

    public void doAfterCompose(Component comp) throws Exception {
        EventListenerUtil.setup(comp, this);
    }

    public void onOK$first(Event e) {
        showResult(e.getTarget().getRoot());
    }

    public void onOK$second(Event e) {
        showResult(e.getTarget().getRoot());
    }

    public void onClick$compute(Event e) {
        showResult(e.getTarget().getRoot());
    }

    private void showResult(Component comp) {
        ((Intbox) comp.getFellow("result")).setValue(
                ((Intbox) comp.getFellow("first")).getValue() +
                ((Intbox) comp.getFellow("second")).getValue());
    }
}

Now, the overhead of boilerplate code is kept to bare minimum, 3 lines of code only, all the rest is domain specific code which directly deals with the event to listen to, the control to receive such event and the business logic to perform thereupon.

Finally, here is the code that did the whole magic behind the scenes. It is a small piece of code employing Java reflection API that will loop through all the declared methods of a class looking for event$id pattern. If one is found, it checks to see it if accepts only one argument of type Event, and if so, it creates an event listener using event name, control id and method name to call that method when that specific event is fired on that control. That work is done behind the scenes and neither the programmer nor the maintainer has to care about it as it is substituted by a little coding convention, which is event$id pattern in method name.

package sysnet.test;
import java.lang.reflect.Method;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;

public class EventListenerUtil {

    public static  void setup(Component comp, final Object listener) {
        Method[] methods = listener.getClass().getDeclaredMethods();
        for (final Method m : methods) {
            String methodName = m.getName();
            int dollarIndex = methodName.indexOf("$");
            if (dollarIndex > 0) {
                Class params[] = m.getParameterTypes();
                if (params.length == 1 && params[0].isAssignableFrom(Event.class)) {
                    String compId = methodName.substring(dollarIndex + 1);
                    Component fellow = comp.getFellowIfAny(compId);
                    if (fellow != null) {
                        String eventName = methodName.substring(0, dollarIndex);
                        fellow.addEventListener(eventName, new EventListener() {

                            public void onEvent(Event event) throws Exception {
                                m.invoke(listener, event);
                            }
                        });
                    }
                }
            }
        }
    }
}


Conclusion

With only a few lines of code it was possible to turn a cluttered code into an easy-write-to-write easy-to-understand code, thanks to the power of Java reflection API and the beauty of the ZK framework.


Ruler.gif


Qamaralzaman Habeek is a senior software developer at Sysnet Software, Algeria. He started software development back in 1990 with VAX COBOL, but soon converted to C and VC++ and finally embraced Java, 10 years later. Since then, he has been developing and preaching system-independent and open-source software for various industries.




Copyright © Qamaralzaman Habeek. This article is licensed under GNU Free Documentation License.