Showing posts with label ZK. Show all posts
Showing posts with label ZK. Show all posts

Wednesday, 4 January 2012

A Simple Property Viewer with Reflection and ZK

Sometimes I need to display a POJO’s properties/attributes. These POJOs may contain dozens of properties making it tedious to invoke all the getXxx() methods. So Java Reflection is a perfect candidate for such a use case.
In the following example, the LoginResponse class contains dozens of fields that I want to display. The formatMethodName() method beautifies the getXxx() method names into more human user friendly names.
import java.lang.reflect.Method;
import com.laws.acc.ws.*;
import com.laws.acc.model.*;

LoginResponse loginResponse=AccSoapClientProxy.getLoginResponse();
void populateRows() {  
 if(loginResponse!=null) {
  Method[] methods = LoginResponse.class.getDeclaredMethods();
  for(int i=0; i<methods.length; i++) {
   
   
   Method method=methods[i];
   //Object arglist[]={};
   if(method.getName().startsWith("get")) {
    Row row=new Row();
    Label label=new Label(formatMethodName(method.getName()));
    label.setStyle("font-style: italic;");
    label.setParent(row);
    new Label(method.invoke(loginResponse, null).toString()).setParent(row);
    row.setParent(rows);
   }
  }
 }
}
/**
 * turn strings such as 'getUserName' into 'User Name'
 */
private String formatMethodName(String name) {
 // the method name should start with 'get' followed by the property name
 // otherwise, ignore the method.
 if(name==null || name.length()<4) return null;
 StringBuffer sb=new StringBuffer();
 
 // skip the first 3 characters, they are 'get'
 for(int i=3; i<name.length(); i++) {
  char ch = name.charAt(i);
  if(i>3 && ch>='A' && ch<='Z') {
   sb.append(" ");
  }
  sb.append(ch);
 }
 return sb.toString().replace("Id", "ID").replace("Dt", "Timestamp").replace("Lre", "LRE").replace("Mvno", "MVNO");
 
}
The GUI component I am using is a ZK Grid with each row showing a property value.


 
// put the above java code here.

 

populateRows();

Sunday, 26 September 2010

JAAS with Tomcat

As JAAS has been included as part of JRE core since Java 1.4, it should be quite prevalent by now. However, I cannot seem to find many online resources that detail the  steps involved to set up JAAS with Tomcat. Here, I outline these steps from a beginner’s perspective.

Step 1 – Implement the JASS interfaces.

The basic interfaces to be implemented are

  1. Principal – there should be two implementations of this interface: One representing the user; another one (or more) represent the roles. The important method of this interface is getName() which returns the name of the user or role that it represents.
  2. LoginModule – this is the main class that usually serves as the wrapper for customised authentication logic. The login() method is where the customised login logic should be invoked.

Once implemented, JAR up these classes including the customised login classes and put it in $CATALINA_BASE/lib.

See the code segments at the end for example implementations.

Step 2 – Configure Tomcat Server

Edit $CATALINA_BASE/conf/server.xml to put in the JAASRealm configuration. As instructed by Tomcat’s Realm-HOW-TO documentation, this can be done at various levels - <Engine>, <Host> or <Context>. I added it inside the <Host> tag:



	
	
...

Notice the userClassNames and roleClassNames are the classes that were created in step 1.

Step 3 – The JRE login configuration

Create a JAAS Login Configuration file. I called it jaas.config and put it under $CATALINA_BASE/conf:
acczk {
  com.laws.acc.jaas.AccLoginModule required;
};

There are two ways to configure the JRE to load the login ocnfiguration file. I prefer to specify it in JAVA_OPTS. For Tomcat, this means to create a setenv.sh or setenv.bat in the $CATALINA_BASE/bin directory. Here is my setenv.bat:

set JAVA_OPTS=-Djava.security.auth.login.config==C:/Tools/apache-tomcat-7.0.2/conf/jaas.config

Step 4 – The Login Form

The login method can be BASIC or FORM-based. I prefer FORM. A vanilla HTML form should do. I happen to be using ZK so my HTML login form is in the file login.zul:






Welcome to ${c:l('client_name')}!

import com.laws.acc.ws.*; String errMsg = null; LoginResponse loginResponse=AccSoapClientProxy.getLoginResponse(); if(loginResponse!=null) { switch(loginResponse.getResultCode()) { case ResultCode.NOT_LOGGED_IN: errMsg="Not logged in."; break; case ResultCode.CONNECTION_ERROR: errMsg="Cannot login to server:\n"+loginResponse.getErrorDescription(); break; default: errMsg="Login failure:\n"+loginResponse.getErrorDescription(); } } Username : Password :
${errMsg}
j_username.focus();
The important part here is the HTML form employing j_username, j_password and j_security_check.

Step 5 - Configure web.xml

This is standard configuration to enable security constraints.

      ACC Security Constraint
      
         Protected Area
         
         /index.zul
	 /secure/*
         
         DELETE
         GET
         POST
         PUT
      
      
         
         Acc User
      
    
	
	  Generic authenticated ACC user
	  Acc User
	
    
    
      FORM
      Form-Based Authentication Area
      
        /login.zul
        /login.zul
      
    
  	
  	120	
  

My JAAS implementation files:

AccLoginModule.java:
package com.laws.acc.jaas;

import java.io.IOException;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import com.laws.acc.ws.AccSoapClientProxy;
import com.laws.acc.ws.LoginResponse;

public class AccLoginModule implements LoginModule {
	// initial state
    private Subject subject;
    private CallbackHandler callbackHandler;
    private Map sharedState;
    private Map options;
    
    private AccPrincipal principal;
    private boolean committed = false;

	@Override
	public boolean abort() throws LoginException {
		if(!committed)
			return false;
		if(principal!=null) {
			logout();
			principal=null;
		}
		return true;
	}

	@Override
	public boolean commit() throws LoginException {
		if (principal == null){
            return false;
        }
        if (!subject.getPrincipals().contains(principal))
            subject.getPrincipals().add(principal);
	// hardcoded role assignment.
        AccRole role=new AccRole();
        if(!subject.getPrincipals().contains(role))
        	subject.getPrincipals().add(role);
        committed = true;
        return (true);
	}

	@Override
	public void initialize(Subject subject, CallbackHandler callbackHandler,
			Map sharedState, Map options) {
		this.subject = subject;
		this.callbackHandler = callbackHandler;
		this.sharedState = sharedState;
		this.options = options;
	}

	@Override
	public boolean login() throws LoginException {
		if (callbackHandler == null)
            throw new LoginException("No CallbackHandler specified");
        Callback callbacks[] = new Callback[2];
        callbacks[0] = new NameCallback("Username: ");
        callbacks[1] = new PasswordCallback("Password: ", false);
 
        // Interact with the user to retrieve the username and password
        String username = null;
        String password = null;
        try {
            callbackHandler.handle(callbacks);
            username = ((NameCallback) callbacks[0]).getName();
            password = new String(((PasswordCallback) callbacks[1]).getPassword());
            LoginResponse response = AccSoapClientProxy.login(username,password);
            if (response.getResultCode()!=0)
                return false;
            principal  = new AccPrincipal(response);
            return true;
        } catch (IOException e) {
            throw new LoginException(e.toString());
        } catch (UnsupportedCallbackException e) {
            throw new LoginException(e.toString());
        }
	}

	@Override
	public boolean logout() throws LoginException {
		committed=false;
		subject.getPrincipals().remove(principal);

		return false;
	}

}
AccPrincipal.java:
package com.laws.acc.jaas;

import java.io.Serializable;
import java.security.Principal;
import com.laws.acc.ws.*;

public class AccPrincipal implements Principal, Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 5002820876845306935L;
	private LoginResponse loginResponse=null;
	
	public AccPrincipal(LoginResponse lr) {
		this.loginResponse=lr;
	}
	
	@Override
	public String getName() {
		return loginResponse.getUserName();
	}

	@Override
	public boolean equals(Object other) {
		if(other==null) return false;
		if(other==this) return true;
		if(other instanceof AccPrincipal) {
			AccPrincipal that=(AccPrincipal) other;
			return loginResponse.getUserId()== that.getLoginResponse().getUserId();
		} else
			return false;
	}
	
	public LoginResponse getLoginResponse() {
		return loginResponse;
	}

	public void setLoginResponse(LoginResponse loginResponse) {
		this.loginResponse = loginResponse;
	}

	@Override
	public int hashCode() {
		return loginResponse.getUserId();
	}
}
AccRole.java:
package com.laws.acc.jaas;

import java.security.Principal;

public class AccRole implements Principal {

	@Override
	public String getName() {
		return "Acc User";
	}
	
	@Override
	public boolean equals(Object other) {
		if (other==null) return false;
		if(other==this) return true;
		if(other instanceof AccRole) {
			return this.getName().equals(((AccRole) other).getName());
		} else
			return false;
	}

	@Override
	public int hashCode() {
		return getName().hashCode();
	}
}

Monday, 8 March 2010

Not Happy, GAE!

Cloud Computing has been a latest buzz word recently and seemingly will be for the foreseeable future. I had registered for Google App Engine (GAE) a while ago but did not do anything with it until last week.

I downloaded the Google Plugin for Eclipse 3.5 (Galileo) and installed it without any problem. I then decided to port one of my web applications written in ZK 3.6.2 with Spring Security 3.0.2 in its simplest form.

I started by creating a GAE application in Eclipse using the Google plugin’s wizard. I then copied WebContent directory from my source project to the war directory of the target GAE project. I modified the deployment descriptors according to instructions from ZK small talk with the following additions:

  1. appengine-web.xml – I added /**.xml in the static file exclude path list since in my application I generate .xml files to feed them to the XML/SwfGauge package.
  2. applicationContext-security.xml – the Spring Security deployment descriptor. I had to add a URL pattern for “/” explicitly due to the different way of handling it by Tomcat (the server I used for developing the web app) and Jetty (the server bundled in Google Plugin).

The modified application then ran quite successfully as GAE application in Eclipse. Of course, anything that involves creating user thread were disallowed and any Java packages/classes that were not supported by GAE were also not allowed. I thought I could work around those problems later.

Then I deployed the application to the real GAE. The deployment process was error free. I was overjoyed and clicked on the URL for my application on the appspot.com. A HTTP 500 – Server error was returned without any explanation.  I did a little fiddling and my first page was displayed if I remove Spring Security, but then all my included pages (I store all my pages in WEB-INF/pages directory) were not shown… I would need at least a few dozen of trial and error before I can pin-point the problem but my internet connection quota is running out fast! So I will call it quits for now.

While cloud computing gives you great promises it is also a shackle and limits your freedom of choice . Although GAE seems to be compatible with many frameworks, the degree of compatibility varies. For example, in the list ZK framework is COMPATIBLE with GAE, but if you look at all the features and packages that are included in ZK you will quickly realise that it is not true – server push requires separate thread and so does jFreeChart and some other bundled packages; captcha uses java.awt.Font (which is not allowed in GAE)… So you’d be better off sticking with GAE’s recommended technology stack – servlet/JSP/JSF + JPA/JDO + JAXB + XML, etc.  This may be OK for new applications but presents a big obstacle for existing large applications which heavily rely on other frameworks.

Sunday, 26 July 2009

Using XML/SWF Gauge

I have been looking for flash widgets to show data in a dashboard. The XML/SWF Gauge has become my best choice so far. It only requires one gauge.swf file which takes in a XML configuration file to instruct it what and how to draw the gauge. I used it to implement a Radar View as part of my dashboard.

I like the simplicity of the XML/SWF Gauge when using it. Yet it generates versatile and visually pleasing results. It also supports dynamic data display and scheduled update/refresh.

Since I used it in a ZK web application, naturally I used ZK to generate the input XML file dynamically. By default XML files are not passed to the ZK Loader to handle. To force it to be passed to ZK Loader I had to add a servlet mapping in web.xml file:


   zkLoader
   *.xml

I can use the same gauge.swf to generate all sorts of views - radar, meter, dial ... all I needed is to generate the appropriate XML file to feed the gauge.swf as shown in the following ZUL file segment.
...


        // ...


 if (view.getName().equals(event.getData())) {
  // recalculate view attributes...
  // refresh:
  gaugeFlash.invalidate();
 }


I do have the following complaints on XML/SWF Gauge:
  1. It does not support concurrent/nested animation.
  2. It does not support event-driven updates (vs. scheduled updates).
If you have come across any good flash gauge packages, please leave a comment.

Thursday, 14 May 2009

Server Push + Event in ZK

I recently implemented a dashboard type of web application in ZK to display a collection of KPI metrics data using various views, for example
  1. Dashboard view - by using some flash dial/meter widgets
  2. Tree view - see screenshot below
  3. Google map view - showing geographical metrics on the map

I want to use ZK's server push feature to dynamically update the views only when the corresponding metric value has been changed and detected from the back end/server side. The examples that are available from ZK small talks invariably pass the widget(s) to be updated to the worker thread and have them updated by the server side. This approach does not quite work in my application for the following reasons:

  1. The various views (see screenshot above) are opened/closed by the user dynamically. Therefore, I don't know which widgets are visible and should be updated;
  2. There are too many widgets to be updated - there could be dozens or hundreds of metrics displayed in the view. So one approach could be to pass the whole view to the server side and have it figure out which widgets to update. I feel that the server side shouldn't be bothered with such a responsibility and the shear number/volume of the widgets involved in the view could render this infeasible.

So I took an alternative approach by combining the server push and event posting that are available in ZK framework.

The main page (main.zul file) contains the code to turn on and off server push as part of its zscript:

void startServerPush() {
 if(!desktop.isServerPushEnabled()){
     desktop.enableServerPush(true);
 }
 MetricsUpdateThread mut = new MetricsUpdateThread(user, desktop);
 mut.start();
}

void endServerPush() {
 if(desktop.isServerPushEnabled()){
     desktop.enableServerPush(false);
 }
}
The MetricsUpdateThread class is shown below:
public class MetricsUpdateThread extends Thread {
 private static final int DELAY_TIME=2000; // 2 second.
 private Desktop _desktop;
 private boolean _ceased;
 
 public MetricsUpdateThread(Desktop desktop) {
  this._desktop=desktop;
 }
 public void updateChangedMetrics() {
  HashSet<Metric> metrics=new HashSet<Metric>();
  // find all changed metrics and put them in the metrics Set
  ...
  if(metrics.size()>0)
    Events.postEvent(new Event("onMetricChanged", null, metrics));
 }
 
 public void run() {
  if (!_desktop.isServerPushEnabled())
   _desktop.enableServerPush(true);
  try {
   while (!_ceased) {
    Executions.activate(_desktop);
    try {
     updateChangedMetrics();
    } finally {
     Executions.deactivate(_desktop);
    }
    Threads.sleep(DELAY_TIME); // Update delay time
   }
  } catch (DesktopUnavailableException due) {
   //System.out.println("Browser exited.");
  } catch (InterruptedException ex) {
   //System.out.println("Server push interrupted.");
  } catch (IllegalStateException ie) {
   //System.out.println("Server push ends.");
  } finally {
   if (_desktop.isServerPushEnabled()) {
    _desktop.enableServerPush(false);
    //System.out.println("Server push disabled.");
   }
  }
 }
}

Notice the Events.postEvent() in the updateChangedMetrics() method, which broadcasts an event if there are any changed metrics. These events are then handled by the corresponding view's zscript. For example, the treeview above has the event handler at its root component like so:
...


 metrics = event.getData();
 for(Metric metric : metrics) {
  tcMovement = win.getFellowIfAny(metric.getId()+"_move");
  if(tcMovement!=null) {
   tcMovement.setImage(Util.constructMovementImage(metric));
    
   tcValue = win.getFellow(metric.getId()+"_value");
   tcValue.setLabel(metric.getValueAsString(metric.getCurrentValue()));
  }
 }

...

...
This approach of combining Server Push and Event broadcasting achieves the effect that I wanted. However, I can't help feeling that it is a bit complicated. So I wonder whether there is a better, simpler or more standard approach to achieve the same user experience in ZK.

Sunday, 26 April 2009

Master-Detail View in ZK

In my latest ZK application, I implemented a typical master-detail view: a split bar in the middle; a tree view on the left and a table/grid on the right. Whenever an item in the treeview is clicked, the grid on the right-hand-side is updated to display the details of the clicked treeview item. The only thing 'special' about this application is that I factored the master (treeview) and details (grid) views as separate files and the main window/page uses <include> to put them together:
<?page title="" contentType="text/html;charset=UTF-8"?>
<zk>
<window border="none" width="100%" height="100%">
 <hbox spacing="0" width="100%" height="100%">
  <include src="analytical.zul"/>
  <splitter collapse="before"/>
  <include src="metricDetails.zul"/>
 </hbox>
</window>
</zk>

The <include> complicates things slightly: the master view which is firing the event has no visibility of who is going to handle the event. Therefore, the event's target field is set to null so that the event is broadcast to all root-level components on the page - including the included details view.

The event sending code is shown below. Note that the treeview of the master view is built using model and renderers and the event sending code is embedded in the renderer.

public class MetricTreeitemRenderer implements TreeitemRenderer {

 @Override
 public void render(Treeitem item, Object data) throws Exception {
  SimpleTreeNode t = (SimpleTreeNode)data;
  Metric metric = (Metric)t.getData();
  ... // construct Treecells
  Treerow tr = null;
  ... // construct the Treerow
  
  final Event e=new Event("onMetricClick", null, metric);
  tr.addEventListener(Events.ON_CLICK, new EventListener(){

   @Override
   public void onEvent(Event arg0) throws Exception {
    Events.postEvent(e);
   }
   
  });
 }
}
The event handling side is part of the included details view file:
<?page title="Metric Details" contentType="text/html;charset=UTF-8"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<zk>
<window id="win" title="Metric Details" border="none" width="100%" height="100%">
<attribute name="onMetricClick">
... handle the event by populating the grid 
</attribute>
<grid id="grid" vflex="true" width="100%" height="100%">

  <columns sizable="true">
   <column label=""/>
   <column label=""/>
  </columns>
  <rows>
...
  </rows>
 </grid>
</window>
</zk>
Note that even though the <window> is inside of the <zk> tag, it is still the root component (since it has no parent component in the .zul file). Also, although the .zul file has been included in the main file, it seems that it still has its own life cycle and its root component is unchanged.

Monday, 1 December 2008

GWT vs. ZK - SLOC Count

I often read about how compact the ZK code was compared to GWT. I couldn't quite believe it. I thought it must have been different design patterns and style of coding used. So I decided to do it myself - I implemented my pet project in both GWT-Ext (GWT 1.4.6 and GWT-Ext 0.9.3 in December 2007) and ZK (ZK 3.5.1 with ZK Studio 0.9.0 in November 2008) and compared the Source Line of Code (SLOC) counts using Code Count tool.

Before I show the SLOC comparison, I need to give a brief introduction of the little demo program that I wrote. The program is a GUI to show various capabilities of the Address package, which has a service tier and DAO implementations behind it. The Address package provides a meta-data driven way of managing address records (see here for the data model). The concerned application here is only the GUI portion. The GUI has the following screens (left=ZK, right=GWT):

CRUD

This screen is used for Create, Retrieve, Update and Delete (CRUD) of the Address records from/to the database.

Find

This screen is used to call the corresponding Address services to find addresses either by keywords or by location.

Compare

This screen is used to call the corresponding Address services to compare two given Address records. Note that the ZK implementation supports drag-and-drop - i.e. I can drag a row from the list of addresses and drop it onto the text box and the text box will be populated with the content of the address (as a string). On the other hand, the GWT implementation does not support DnD because at the time of writing it, GWT-Ext did not support this kind of DnD yet (and I am not about to rewrite it using new version of GWT-Ext now considering Sanjiv has left the project... but that's another story).

Preferences

This screen is used to manage user preferences of the GUI application. Cookies are used as the storage.

In my SLOC count, I divided the source code into the following groups:

  1. Main View - this includes the main screens: CRUD, Find and Compare tab panels.
  2. Preference View - the user preference window
  3. Model/Controller - the code behind the screen components, including models, event handlers, interaction with the service tier, helper/utilities
  4. Splasher - this is the Splasher screen displayed at the beginning of the application. Only ZK version has implemented this (using index.zul and automatically forwarding to the main view after initialising the Address service); other versions do not have this screen.

Note that in ZK, all the views are a result of drag-and-drop from the components palette with modifications by hand.

The physical and logical SLOC counts are shown below.

The results speak for themselves.

UPDATE [2008-12-08]:

GWT/GWT-Ext screenshots have also been added after the ZK ones (i.e. GWT screenshots are on the right-hand side).

Thursday, 27 November 2008

ZK - First Impression

I first heard of ZK from several developer forums, where one of the authors of ZK posted articles comparing ZK with GWT trying to prove that ZK was superior. Ever since then, I have been wanting to learn/use ZK for several months now.

I was pleasantly surprised by the maturity and high quality of ZK from the first moment I started using it - I didn't expect it to have a drag-and-drop graphical editor - something that not many free web frameworks have out of the box. I believe it is a real contender of GWT.

The praises of ZK can be found easily and I will not repeat them here. Instead, I will outline the reservations that I have about ZK hoping to get advices from the community.

ZK Studio Installation

When I followed download instructions on ZK site to install the ZK Studio directly from Eclipse, it turned out to be an older version of the plugin. It took me a while to find out why my ZK Studio did not have the ZUL Graphical Editor mentioned in the documentation. I had to download the plugin (v0.9.x) and install it onto Eclipse manually.

ZUL Graphical Editor

The ZUL Graphical Editor is not really an editor at the moment. It only serves as a previewer. I believe ZK team is actively working on improving the editor right now.

Debugging

I am not aware of any special debugger for ZK. If something goes wrong, I have to inspect the web server's log file to get the stack trace - cannot jump to the line of source code automatically. Moreover, if the error occurs at the ZUL script, the trace shows BeanShell exceptions, which can be harder to locate the real position of the error in the ZUL file.

Centralised ZScript File

I understand that zscript can be isolated in its own file and be included into the .zul file, so that the script is not littered in the presentation code. However, I find that to completely separate the script and the ZUL code is not practical. For example, to specify the a renderer for a component, I need to instantiate the renderer and assign it to a variable, then put the variable to the components renderer attribute. For example,
<zscript>
import com.laws.address.zk.*;

renderer=new AddressRenderer();
</zscript>
   <listbox id="crudAddressList" mold="paging" pageSize="10" 
   itemRenderer="${renderer}">
    <listhead sizable="true">
     <listheader label="ID" width="30px"/>
     <listheader forEach="${afts}" >
      <attribute name="label">${each.name}</attribute>
     </listheader>
    </listhead>
   </listbox>

However, the bit of script that instantiates and assigns the new renderer cannot be separated into another file (unless a function is created to do it, which looks odd and impractical).

Event Handling

The event handling in ZK is strange - the sender has to specify the target for the event. The whole idea of events is so that the sender (or publisher) is not aware of who is going to consume the event, or how many consumers, and what the consumer will do with it. But in ZK, the target of the event is specified up front by the sender - i.e. the sender knows who should receive the event. I shall elaborate this point in another post in more detail.

To be fair, ZK does allow the target of the event to be null when posting an event. However, the event is only accessible from top-level components in this scenario.

Server Round Trips for Static Contents

Unlike GWT, even static contents in ZK will involve server decision (a bit like Echo 2). For example, upon clicking a button or menu, my application pops up an 'About' message box displaying static contents. The processing of the button click and the display of the message box involves the server and incurs a round-trip to the server. This certainly has performance penalty and puts presentation logic and load onto the server (which, IMO should be placed in the client tier - i.e. the browser). I know, there has been debate about whether the GWT way is better. I personally prefers the GWT way - especially considering it is broadband era now.

Overall, my experience with ZK has been quite positive. I think it is among my top 3 web RIA frameworks.