Best Model-View-Controller Patterns

Simon Massey, AVP, Marsh, The world's leading insurance broker and strategic risk advisor
June 9, 2008

Version

Applicable to ZK 3.0.5 and later.

Why MVC?

Consider the following code which does not use an MVC pattern:

<window border="normal">
   <grid width="80%">
       <rows>
         <row>onChange1 textbox: 
            <textbox id="source1">
            <attribute name="onChange">
                copy.value = source1.value
              </attribute>
            </textbox>
          </row>
          <row>onChange2 datebox: 
            <datebox id="source2">
            <attribute name="onChange">
                copy.value = source2.value.toString();
              </attribute>
            </datebox>
          </row>
          <row>output: 
            <textbox id="copy" readonly="true"/>
          </row>
        </rows>
    </grid>
</window>

In the example above as the functionality was so simple it is easy to see and understand it as a whole. However, in real applications, the requirements is much more complex for the following reasons,

  • Complicated event processing logic that takes data from several components, calls out to business services, and then updates several different screen components.
  • Sophisticated ZK applications define desktops that have a number of pop-up windows, included page files, and macro components.
  • Events are fired from components that cause updates to components defined within completely different ZUML source files.

Here are some possible risks to build a real-world application without using MVC.

  • Reading such code to understand how the application works can be hard.
  • Hard for a team of programmers to easily find where to make changes within the codebase.

Thus, when you are faced with writing a sophisticated user interface then it can be a good investment in time and effort to move all of the event processing code one or a small number of classes that control application behavior.

A Simple MVC Example

In terms of ZK developer the MVC patterns is made up of the following items. The 'Model' is your business objects and business services. The 'View' is the set of Components in the desktop defined in ZUML (zul, zhtml) files containing no event processing logic. The 'Controller' is a pure Java class that is registered on an EventListener one or more Components in the desktop.

Here is the example above refactored to take the MVC approach:

<window border="normal" apply="MyController">
        <grid width="80%">
                <rows>
                        <row>textbox: <textbox id="source1" forward="onChange=onSource1"/></row>
                        <row>dateBox: <datebox id="source2" forward="onChange=onSource2"/></row>
                        <row>output: <textbox id="copy" readonly="true"/></row>
                </rows>
        </grid>
</window>

The window in the page has apply="MyController" that references the following Java class:

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Textbox;

public class MyController implements Composer {

        protected Textbox copy;
        protected Textbox source1;
        protected Datebox source2;
        
        public void doAfterCompose(Component window) {
                copy = (Textbox) window.getFellow("copy");
                source1 = (Textbox) window.getFellow("source1");
                source2 = (Datebox) window.getFellow("source2");
                window.addEventListener("onSource1", new EventListener() {
                        public void onEvent(Event event) throws Exception {
                                copy.setValue(source1.getValue());
                        }
                    });
                window.addEventListener("onSource2", new EventListener() {
                        public void onEvent(Event event) throws Exception {
                                copy.setValue(source2.getValue().toString());
                        }
                    });
        }
}

Adopt Forward, and Apply Concepts

In the refactored example we see that the zul file has no event processing code within it. Instead the input components each have a "forward" attribute defined. This tells ZK to forward on their onChange events onto a different method on a different object. By default the target of the forward event will be the space owner of the components which in this case is the enclosing window component.

The window component has an "apply" attribute. This specifies to use an instance of the MyController class to initialize the window. Within the Java code of the MyController class we have a doAfterCompose method which is called after the desktop has been assembled. The window component is passed into that method. Within that method we lookup and store references to the three components on the page using the getFellow method. Then two event handlers are registered on the window object. The event handler names match the two "forward" attribute on the components in the zul file. In the above example there is no explicit Model code or class as our sample code is a stateless application. If it were a real program the MyController would make calls to business objects that would represent the model of the application.

More Codes?

At first look the refactored code appears to be a lot more effort with very little gain. We have replaced two lines of zscript event handler code with a class that takes 30 lines of code. However the two lines of code in the first example are not actually executed as two lines of Java code. Lets go back and revisit those original event handlers:


copy.value = source1.value

copy.value = source2.value.toString();

These two event handlers look like Java but they are not compiled and run as Java. The are actually zscript that is run in one of a choice of serverside scripting engines. By default zscript is run using the BeanShell interpreter. We can instead choose to use Rhino the Javascript interpreter written in Java which comes with JDK1.6 JSR223 and many J2EE containers such as Websphere 6. Simply ensure that zkmax.jar and js.jar from the Rhino project are on the classpath and use this version of the orginal code:

<?page zscript-language="JavaScript"?>
     <window border="normal">
            <grid width="80%">
            <rows>
                <row>onChange1 textbox: 
                  <textbox id="source1">
                      <attribute name="onChange">
                        copy.value = 'js says: '+source1.value
                      </attribute>
                  </textbox>
                </row>
                  <row>onChange2 datebox: 
                  <datebox id="source2">
                      <attribute name="onChange">
                        copy.value = 'js says: '+source2.value;
                      </attribute>
                  </datebox>
                </row>
                <row>output: <textbox id="copy" readonly="true"/></row>
            </rows>
            </grid>
      </window>


This version of the code has javascript strings in it such as 'js says: ' as the event handler is run on the server using the Rhino interpreter in js.jar. Very similar technology is used in the very first example that makes us of the BeanShell interpreter in bsh.jar. The event handler is parsed by the interpreter at runtime. Then the parsed script is usually run via Java reflection and introspection. The single line of zscript is actually acted out by very many more classes and methods within the interpreter engine. With a real application that has big event handlers running them in an interpreter gives a runtime overhead. If the interpreter encounters an error you cannot attach a debugger to easily debug the script. Typically you use System.out.println output to debug your script. Once again this is all very managable in small event handlers. Switching to the MVC pattern the event handlers that are pure Java gives you the option to use your favourite Java debugger to allow you to inspect and modify the state of the running application at a breakpoint.

Shared Controller

Back with our MVC example we should consider where the controller gets instantated. If we use a classname in the apply attribute such as apply="MyController" then a new controller object is instantiated whenever the page is loaded or reloaded. We may chose to instantate a controller once and only once when the user zk session is created. To do this we can use a SessionInit class such as:


 public class MySessionInit implements SessionInit {
        public void init(Session session, Object request) throws Exception {
                MyController myController = new MyController();
                session.setAttribute("myController", myController);
                return;
        }
 }

In the init method we could do Spring bean or JNDI look-ups to get business services and business objects to pass to the controller object. To configure ZK to run the session initializer we add an entry to zk.xml such as:


        <listener>
                <description>MyController Session Initialiser</description>
                <listener-class>MySessionInit</listener-class>
        </listener>
        

Within the zul file we change the apply attribute of the window component reference the controller object in the session:


<!-- WARNING ONLY USE sessionScope FOR COMPOSER IF IT DOES NOT HOLD REFERENCES TO COMPONENTS -->
<window id="myWindow" border="normal" apply="${sessionScope.myController}">
        <grid width="80%">
                <rows>
            <row>textbox: <textbox id="source1" forward="onChange=myWindow.onSource1"/></row>
            <row>dateBox: <datebox id="source2" forward="onChange=myWindow.onSource2"/></row>
                        <row>output: <textbox id="copy" readonly="true"/></row>
                </rows>
        </grid>
</window>

In that last version of the code the forward attributes were changed to reference 'myWindow' which is the ID given to the window component. This is not needed but it does make the code clearer. We may also chose to apply the same controller to the the main window and a number of pop-up windows. The doAfterCompose can then check the ID of the window in question and register its components and EventListeners.

Note the warning in that code. It warns that you should not use a controller held in the users session if the composer object holds references to Components in the desktop. What and why is that? Users might open up a new window by using Ctrl+N on IE or FF. If a user does that they will get two independent desktops with two versions of everything, one in each browser window. If the user fires events in the first Desktop, then the composer tries to update Components in the second Desktop things will go wrong. A solution to this issue would be to make your composer stateless with respect to components:


import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Textbox;

public class MyController implements Composer {

        public void doAfterCompose(Component window) {
                window.addEventListener("onSource1", new EventListener() {
                        public void onEvent(Event event) throws Exception {
                                Textbox copy = (Textbox) event.getTarget().getFellow("copy");
                                Textbox source1 = (Textbox) event.getTarget().getFellow("source1");
                                copy.setValue(source1.getValue());
                        }
                });
                window.addEventListener("onSource2", new EventListener() {
                        public void onEvent(Event event) throws Exception {
                                Textbox copy = (Textbox) event.getTarget().getFellow("copy");
                                Datebox source2 = (Datebox) event.getTarget().getFellow("source2");
                                copy.setValue(source2.getValue().toString());
                        }
                });
        }
}

In this latest version of the code the getFellow calls return references to components that are scoped to the event handlers. Imagine onSource1 firing in one browser window then onSource2 firing in the second browser window. With this latest code our single composer held in the websession behaves well. Naturally any business objects that it calls out to during event handlers should be stateless or singleton to support being shared across two concurrent user desktops.

Another approach to the problem of a user opening a second window is go back to the technique of to passing the class name to the apply attribute as discussed above. Then we may return to the version of our controller that does hold references to components in the desktop as a composer can never be shared between desktops. Then we might choose to use a SessionInit object to obtain singleton or statless business service objects that we cache in the session that we would resolve in doAfterCompose as follows:


import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Textbox;

public class MyController implements Composer {

        protected Textbox copy;
        protected Textbox source1;
        protected Datebox source2;
        protected MyBusinessStatelessService service;
        
        public void doAfterCompose(Component window) {
                service= (MyBusinessStatelessService)
                        window.getSession().getAttribute("service");
                copy = (Textbox) window.getFellow("copy");
                source1 = (Textbox) window.getFellow("source1");
                source2 = (Datebox) window.getFellow("source2");
                window.addEventListener("onSource1", new EventListener() {
                        public void onEvent(Event event) throws Exception {
                                copy.setValue(service.getByText(source1.getValue()));
                        }
                });
                window.addEventListener("onSource2", new EventListener() {
                        public void onEvent(Event event) throws Exception {
                                copy.setValue(service.getByDate(source2.getValue()));
                        }
                });
        }
}

In the upcoming ZK version 3.0.6, some utility classes and methods that helps developers to bind events and variables automatically:


public class MyController extends GenericAutowireComposer {
        protected Textbox copy;
        protected Textbox source1;
        protected Datebox source2;

        public void onSource1(Event event) throws Exception {
                copy.setValue(source1.getValue());
        }

        public void onSource2(Event event) throws Exception {
                copy.setValue(source2.getValue().toString());
        }
}

You can see that the MyController.java is much more clean. You declare protected Component member variable in your Java class using variable names that match the IDs in the ZUML page. The behaviour of GenericAutowireComposer is to automatically "inject" the correct Components into you class using reflection and introspection. You write your onXxx event handler methods and GenericAutowireComposer will automatically "add" them as event listeners on the component that you apply your composer to. If you cannot inherit from GenericAutowireComposer directly then you can call Components.wireVariables() and Events.addEventListeners() to achieve same results.


The ZK DevGuide states "ZK doesn't enforce developers to use MVC or other design patterns. Whether to use them is the developer's choice"

Simon Massey  is a AVP working for Marsh (London, UK). He is also the host deveoper of the ZKFoodToGo project for ZK.forge .
Copyright © Simon Massey. This article is licensed under GNU Free Documentation License.
Comments
 
Mike
2008-06-09

I like the MVC approach. The codes are much more readable to me without zscript. Thanks for the valuable article.

QamarAlZaman Habeek
2008-06-09

Even this small piece of code is not needed:

forward="onChange=onSource1"

you can just let the concerned component listen for itself, hence, further simplifying the code, as follows:
..............................
<row>textbox: <textbox id="source1" /></row>
........................

..........................
source1.addEventListener("onChange", new EventListener() {
public void onEvent(Event event) throws Exception {
copy.setValue(source1.getValue());
}
});
......................

Simon Massey
2008-06-09

Mike,

I have found the 'handler in-page' approach is very productive. It is a sort of "serverside DHTML". Typically with the in-page approach a zscript at the top of the page can define a lot of script functions that sort of collect the logic together. Using the spring variable controller you can just reference spring beans in your functions by name and they are automatically resolved. As spring plays will with JNDI you can fly along. ZKs databindings can also make in-page more attractive by having to do less initialization code. Checkout the latest version of the zk foodToGo application described in another smalltalk on this site to see the in-page approach. I think that both approaches are good tools in the box and people should pick the right tool for their team and the complexity of the UI.

Simon

RobertPic
2008-06-10

Hi Simon

I've learned something new: I did not expect that the apply-attribute can handle classes via VariableResolver like this: apply="${sessionScope.myController}

But, my Composer would look like this:

public class MyController extends GenericComposer {

        protected Textbox copy;
        protected Textbox source1;
        protected Datebox source2;
        
        public void doAfterCompose(Component window) throws Exception {
                copy = (Textbox) window.getFellow("copy");
                source1 = (Textbox) window.getFellow("source1");
                source2 = (Datebox) window.getFellow("source2");
                super.doAfterCompose(window);
        }
        
        public void onSource1(Event evt) {
            copy.setValue(source1.getValue());
        }
        
        public void onSource2(Event evt) {
            copy.setValue(source2.getValue().toString());
        }
}

However, i do not use the composer. I forward my events to the rootwindow. The problem is AnnotationDatabinding. I could not bind the domainbeans to the controller/Composer. I would prefer one class like a managed bean in JSF for dataholding and event-processing (controller).

Your smalltalk shows a good way for a shared (Event)controller. I miss that option for a shared (Data)controller.

Robert

henrichen
2008-06-10

With EL expression on apply-attribute, in fact, we can now "inject" ZK components into Spring-managed-object.

In Spring's configuration, we can define our controller as a Spring bean.

<bean id="mycontroller"
    class="org.mypackage.MyController"
    p:datasource-ref="mydatasource" ... />
...

And in the zul page, we use the ZK's Spring variable resolver and the apply-attribute EL to use the Spring bean "mycontroller" directly.

<?variable-resolver class="com.potix.zkplus.spring.DelegatingVariableResolver"?>
<window apply="${mycontroller}">
...
</window>

So now we have the Spring-managed-object "Controller" to access both "View" and "Model" and do the business logic as it should. This is a good and natural MVC pattern.

Tuan Nguyen Anh
2008-06-18

I’m very eager with “best model view controller patterns” small talk. Based on the article, I design the new MVC model myself. Here are the code sample:

1. The controller code:

public class FormGroupController extends GenericComposer {
private FormGroupView viewComponent ;
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
viewComponent = new FormGroupView((Window)comp);
}
public void onEdit(Event event) throws Exception {
}
public void onCopy(Event event) throws Exception {
}
public void onDelete(Event event) throws Exception {
}
}

2. The View Component:

public class FormGroupView extends Window {
Window win;
public FormGroupView(Window win){
this.win = win;
}
public void showList(List groupList) {
Listbox listbox = (Listbox)win.getComponent("lbForm");
listbox.getItems().clear();
Iterator it = groupList.iterator();
while (it.hasNext()) {
EzFormGroupVO item = (EzFormGroupVO) it.next();
Listitem li = new Listitem();
li.setContext("menuPopupGroup");
li.setValue(item);
li.appendChild(new Listcell(item.getGPGNAM()));
li.appendChild(new Listcell(item.getGPTEXT()));
listbox.appendChild(li);
}
}
}.
With the above design, I would like to separate the Controller (event processing) and the View Component (change/modify interface based on the event).

Also, the design was applied to my project and it make me happy. But, I have some concerns about the following items:
1. The window component is passed to doAfterCompose() method and it is stored as instance variable in the FormGroupView class, I would like to know in the multi threads environment, this window is always the is the object which was passed to doAfterCompose() method before.
2. In this design, the instance variable viewComponent can be accessed simultaneously in the multi threads environment?. The above code is thread safe?
Please help me to resovle my concerns. Thanks in advances.

henrichen
2008-06-18

Hi Tuan Nguyen Anh,

The "apply" composer is called only once when the "page" is first loaded and rendered. So the passed in window component is the one created when the page is loaded and rendered. Sharing an instance of composer must be very careful. The shared composer is generally stateless.

If the composer is instantiated from class name, then you can keep component state inside the composer.

Notice that composer actually works like an aspect callback. The ZK engine will call back to the doAfterCompose() method of the composer and that is all. If you implement your own composer, the thing guaranteed is that the doAfterCompose() will be called. However, if you define your composer by extending GenericComposer, the doAfterComposer() code in GenericComposer actually adds this composer as the EventListener of the passed in component so the component will refers the composer.

Tuan Nguyen Anh
2008-06-19

Hi Henri Chen,
Thanks for reply quickly. Can you explain to me the meaning of "the composer is instantiated from class name". I really don’t have enough knowledge to understand how to ZK process events in multi threads environment. Thus, I need you help to decide my design is safe.
At the moment, I have used ZK to develop our enterprise web application. Thus, I would like to deeply understand about ZK architecture before I make the decision our company will use the ZK framework with license. Would you help me about direction, documents? The current guides, documents on your website is enough good, but it’s rather basic & common. I need the more details and clearly documentation about ZK framework.

Again, Thanks for your help and support.

Simon
2008-06-20

Tuan Nguyen Anh,

ZK, as per the developer guide and developer reference, by default takes steps to prevent more than one ajax event running in parallel. This is to prevent risk of concurrent ajax requests trying to modify the desktop state in parrallel. In that sense ZK follows the correct patterns for ajax and defends against concurrent ajax processing modifying your serverside desktop state.

To answer your question about "the composer is instantiated from class name". If you provide a classname (as in apply="com.x.y.MyComposerClass" not apply="${myComposerObject}") then ZK instantiates an object of the named class on each separate zuml page load. As the servlet container enforces that page loads happen on different servlet threads (like loading any jsp page) it means that no two threads can share the same instantiated composer object that zk creates on your behalf. Then it is down to you how your composer object maintains or users references to objects held within the session and how different events processed sequentially (enforced by zk) will update the objects in the session.

There is a risk that henrichen has brought to my attention around using apply="${sessionScope.myComposerObject}". If the user opens a new window using Ctrl+N she then has two whole parallel desktops both trying to interact with the same controller. At this wiki entry I have updated the article to discuss this subtle matter:

http://en.wikibooks.org/wiki/ZK/How-Tos#Model_View_Controller_Pattern

Thanks again henrichen for your input on this.

ZK is built upon the servlet standards and is deployed as standard j2ee components. As with standard servlet/jsp you also have to consider what happens if the user opens two browser windows and keeps on alternating between them to work in two areas of your webapplication concurrently. You have to think carefully about what you are storing and updating in the users httpsession object and whether things would get confused if the user tries to use two windows concurrently. That is a normal (if a bit obscure) j2ee consideration.

Simon Massey

Tuan Nguyen Anh
2008-06-21

Simon,
Now, i clearly understand how to implement MVC model with ZK, thank you very much for help.
Cheers,
Tuan Nguyen

PfisterA
2008-07-01

Hi

great stuff. Thats the perfect way to integrate ZK into Grails.

Thanxs

Alassane
2008-07-03

Hello,
I liked the article.
In our current project we are using ZK framework.
But we implemented the MVC pattern using the "use" attribute of the window component.
What about this approach ?
What are the differences between this and using "apply" attribute ?
Thank yous

henrichen
2008-07-19

See this

injecting spring beans in ZK components

henrichen
2008-07-19

and this is a discussion regarding the differences between "use" and "apply".

The Trilogy of ZK's MVC Adventure

simon
2008-08-08

The "use" approach pre-dates the "apply" approach and the "forward" feature. You can use "use" and "forward" to get the same effect. However with the "apply" approach your controller implements the Composer interface and for the "use" approach you must subclass a zul component; usually Window. From a purest perspective the Window class is very much a 'View' class. It would seem to me a bit cleaner to create your own controller class in our own business logic package that implements the Composer interface rather than subclass Window and add a lot of business logic into your Window subclass. That is however more a matter of style than one of function.

Where things get very interesting is that ZK provides automation to assist with the "apply" approach. If you subclass their GenericAutowireComposer to create your Composer then automatically member variables that match IDs inside of your window will be resolved and set by the logic within your chosen parent classes . It will also register all of your onXxx methods as event listeners on the component in question. You cannot take advantage of such a thing if you use the "use" approach as their your parent class is a Window component and it does not have any specific automation to make the (optional) MVC pattern easier to implement. Quite rightly the Window object specializes in being a window and is not to be warped into being a "God Object" with too much mysterious power.

Going one step further if you like to use the PicoContainer or HiveMind rather than rather than Spring you could easily subclass GenericAutowireComposer and extend its functionality to find member variables within your subclass via introspection that have a name that match a beans defined within PicoContainer/HiveMind. Your new GenericAutowireComposer type It can then get the beans from the IoV container and set them automatically. Then you would not have to write any repeated logic to resolve your applications components but have them automatically injected into your controllers but simply subclassing your new class and defining an appropriate member variable that matches your business logic beans.

simon
2008-08-08

As at 3.0.7 GenericAutowireComposer has a new capability so that you don't have to add "forward" attributes to the components who's events you want to fire on your controller. Instead in your controller define event handlers with the syntax as "public onChange$myTextbox(Event event)" or "public onSelect$myListbox(Event event)" where the string after the $ symbol is the name of the component and the bit before is the event that you want forwarding and programmatically the forward will be added to the component in question to cause your onChange$myId method to be called. Take a look at the latest version of the code at http://zk1.svn.sourceforge.net/viewvc/zk1/trunk/zk/src/org/zkoss/zk/ui/Components.java to learn learn more about this class.

Simon Massey
2008-08-11

ZK 3.0.7 provides even more automation via the GenericForwardComposer to make the MVC easier to code. I have document the new approach at http://en.wikibooks.org/wiki/ZK/How-Tos#Model_View_Controller_Pattern and I have also updated the Spring entry on the wiki as to how to have it manage your Controller and Model classes.

 
 
Leave a Reply
 
Name (required)
Mail (will not be published) (required)
Website
(Case Insensitive)
Bold textItalic textUnderLine textSource CodeHorizontal rulerExternal Link