Tweak ZK to make event processing call groovy’s invokeMethod
My current project needs a very dynamic and fast pages without refreshs, or using the buzz word ajax, to accomplish this requirement I'm using ZK Direct RIA. As ZK heavily uses ajax requests with unpredictable url I need another way to protect the controller's actions (composer's method) instead of url. Using @Secured and groovy's invokeMethod I was able to protect each method of any class (please read the post to get a better picture of what I'll discuss) which works fines if the method call is from a groovy code, ZK is fully implemented in java though.
When a user clicks on a button, or does anything that generates events, zk process it and call the corresponding method in the composer class. ZK events processing executes this code when it find the method to call:
mtd.invoke(controller, new Object[] {evt});This uses Java Reflection API and completely bypass groovy's invokeMethod, what we need to do is use groovy's InvokerHelper class instead of direct calling the method using reflection.
InvokerHelper.invokeMethod(controller, mtd.getName(), new Object[] {params});Now that we know how to do it for any kind of java code let's dig into the problem with ZK event and find out where we should use InvokerHelper.
Implementing InvokerHelper in GrailsComposer
The GrailsComposer in ZKGrails plugin is a subclass of GenericForwardComposer that changes the default char separator from '$' to '_', because groovy already uses the '$' with another meaning, and allows the use of a closure as the doAfterCompose method. So when you create a new composer using grails create-composer <composer name> your new class extends GrailsComposer. Let's create a composer and protect two of it's buttons.
import org.springframework.security.annotation.Secured
class MainAreaComposer extends GrailsComposer {
private Button btnEditText // automatically wired from the zul file
private Button btnSaveText
private Button btnCancelText
def afterCompose = {
// loads the current text
}
@Secured("mainArea_editText")
public void onClick_btnEditText() {
// shows the rich text editor to edit the text
btnSaveText.visible = true
btnCancelText.visible = btnSaveText.visible
btnEditText.visible = false
}
@Secured("mainArea_editText")
public void onClick_btnSaveText() {
// saves the text, hide the editor and load the new text
btnSaveText.visible = false
btnCancelText.visible = btnSaveText.visible
btnEditText.visible = true
}
public void onClick_btnCancelText(){
// hide the editor and loads the text without any modifications
}
}The idea here is that only some users have access to edit the text. ZK is fully ajax based and every event cames from client through javascript request. Because of this even if the btnEditText is invisible a malicious users could forge a request to call the method - not easy, but possible - the use of @Secured is to assure that an attempt to call this method from an unauthorized user will fail.
If you run your app using ZKGrails 0.7.5, the current version for Grails 1.1.1, and test if an unauthorized user fails when call the method - just make the button always visible to test - you will see that nothing is blocked no AccessDeniedException is thrown, and of course this isn't good. Humm, and how could we solve this? How to make zk event handling aware of the dynamic methods?
My first thought was "ZK is open source, what about reading it's code to understand the event system?" and there I go, downloaded the source and start reading how ZK maps the methods name to components event, in other words how it does this magic:
public void onClick_btnCancelText() {// event code}The above method is the same as the not so nice:
btnCancelText.addEventListener("onClick", {// event code} as EventListener)Reading the source code I find out that this kind of event is a ForwardEvent but I couldn't find where the original method is called until I look the class diagram for GrailsComposer:
And there was the solution GenericEventListener onEvent method, I've finally found where the original method (onClick_componentId) is invoked and now we just need to override onEvent in GrailsComposer, this is the implementation:
org/zkoss/zkgrails/GrailsComposer.java
/**
* Overrides GenericEventListener to use InvokerHelper to call methods. Because of this the events are now
* part of groovy's dynamic methods, e.g. metaClass.invokeMethod works for event methods.
* @param evt
* @throws Exception
*/
@Override
public void onEvent(Event evt) throws Exception {
final Object controller = getController();
final Method mtd = ComponentsCtrl.getEventMethod(controller.getClass(), evt.getName());
if (mtd != null) {
if (mtd.getParameterTypes().length == 0) {
InvokerHelper.invokeMethod(controller, mtd.getName(), null);
} else if (evt instanceof ForwardEvent) { //ForwardEvent
final Class paramcls = (Class) mtd.getParameterTypes()[0];
//paramcls is ForwardEvent || Event
if (ForwardEvent.class.isAssignableFrom(paramcls)
|| Event.class.equals(paramcls)) {
InvokerHelper.invokeMethod(controller, mtd.getName(), new Object[] {evt});
} else {
do {
evt = ((ForwardEvent)evt).getOrigin();
} while(evt instanceof ForwardEvent);
InvokerHelper.invokeMethod(controller, mtd.getName(), new Object[] {evt});
}
} else {
InvokerHelper.invokeMethod(controller, mtd.getName(), new Object[] {evt});
}
}
}Just replacing method.invoke to InvokerHelper.invokeMethod (lines 13, 19, 24 and 27) is enough to make the methods actually secured. Open your GrailsComposer class and add this method to the class, this is all you need to do. If you don't understand how the @Secured is now working, read the previous post which is about the implementation of @Secured.
I've submitted this as a patch to Chanwit, the plugin's maintainer, and ZKGrails 1.0 will have this overridin onEvent out-of-the-box. No need to change the plugin source code anymore, great news.

October 28th, 2009 - 05:54
A little bit of the topic but does ZK’s Groovy interpreter support compilation? How can I turn it on? Thanks.
October 28th, 2009 - 14:38
I don’t know if understand what is the ZK’s Groovy interpreter. I’ll try to explain how it works for me.
Groovy and Java are fully compatible, we can extend a java class in a groovy one and vice-versa. So because of this we can write a groovy class that extends GenericForwardComposer and this class will act exactly as a java one that extends this same class, when you build your project the groovy code will be compiled to bytecode normally.
November 1st, 2009 - 02:16
Hi,
For both cases through interpreter and Grails composer, Groovy classes are always compiled before execution.
October 29th, 2009 - 10:28
Great post!
November 3rd, 2009 - 16:57
Thanks Edgar