Here’s an example, where I have a method in an EJB that has a role restriction…
@RolesAllowed({"admin", "entryclerk"})
public void doSomething(String action)
{
...
}
Whenever the doSomething thread was called from an event, such as a button click event, it would fail with an error like the following. Take note of the bold sections, indicating the error, and the fact that it is a ZK thread.
Message : Unauthorized Access by Principal Denied Exception : javax.ejb.EJBAccessException Stack : javax.ejb.EJBAccessException: Unauthorized Access by Principal Denied at org.apache.openejb.core.stateful.StatefulContainer.checkAuthorization(StatefulContainer.java:706) at org.apache.openejb.core.stateful.StatefulContainer.businessMethod(StatefulContainer.java:479) at org.apache.openejb.core.stateful.StatefulContainer.invoke(StatefulContainer.java:277) at org.apache.openejb.core.ivm.EjbObjectProxyHandler.businessMethod(EjbObjectProxyHandler.java:217) at org.apache.openejb.core.ivm.EjbObjectProxyHandler._invoke(EjbObjectProxyHandler.java:77) at org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:281) at $Proxy47.isAuthenticated(Unknown Source) at com.example.main.SubmitButton.postTransaction(SubmitButton.java:65) at com.example.main.SubmitButton.onEvent(SubmitButton.java:186) at org.zkoss.zk.ui.impl.EventProcessor.process0(EventProcessor.java:197) at org.zkoss.zk.ui.impl.EventProcessor.process(EventProcessor.java:141) at org.zkoss.zk.ui.impl.EventProcessingThreadImpl.process0(EventProcessingThreadImpl.java:519) at org.zkoss.zk.ui.impl.EventProcessingThreadImpl.run(EventProcessingThreadImpl.java:446)
Threading is not allowed in EJB, and as a result, the user and role security was not propagated to the EJB calls I was making, as they calls were being made from within an entirely different thread than what was being managed by the EJB container. The solution to this problem, is to disable ZK event threads. ZK threads are enabled by default, but may be disabled in zk.xml, as follows.
Once the threads are disabled, the access denied errors disappear.
]]>
I thought it important to have some useful features that make sense for what I’ve seen in status bars.
Future features might include…
We first, of course, need to setup the actual status bar. So, right before the end of the window, we do just that, with a “textbox”. Now, there’s no reason you can’t use some other type of control, if it makes sense to.
The text box is implemented by a custom class, which we’ll delve into in a moment. We also have a ZK timer, for implementing the automatic clearing feature, because it is illegal to access ZK elements from outside of a ZK event. i.e. You cannot write your own thread to do it at a later time, as ZK will throw an IllegalStateException.
We’ve kept the interface very simple, just two methods. For more information, read the javadoc comments.
package com.example.system;
/**
* Handles setting of status bar messages. All calls to this object must be
* done from within the ZK framework, such as inside an event.
*
* Hides the details of what component is actually a status bar. It could be a
* textbox, or something else, but we don't want to be dependant on any specific
* type of control, in case it changes in the future.
*
* Created : Jan 31, 2010 1:39:37 AM MST
*
* Modified : $Date$ UTC
*
* Revision : $Revision$
*
* @author Trenton D. Adams
*/
public interface IStatusBar
{
/**
* Sets the status bar text for the time period indicated.
*
* @param statusText status text
* @param timePeriod delay in seconds, before the status bar will be
* cleared.
*/
public void setStatus(final String statusText, final int timePeriod);
/**
* Sets the status bar text. Pass null to clear.
*
* @param statusText status text or null to clear
*/
public void setStatus(final String statusText);
/**
* @return the status text of the status bar.
*/
public String getStatus();
}
Essentially, all we are doing with the implement is as follows
package com.example.system;
import org.apache.log4j.Logger;
import org.zkoss.zk.ui.Session;
import org.zkoss.zk.ui.ext.AfterCompose;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Timer;
/**
* StatusBar handles setting the status bar text, and clearing it after a given
* timeout. Implements IStatusBar, to hide the details of what a status bar is
* from the client code. We use a textbox, but we could change that, who knows.
*
* Created : Jan 31, 2010 12:42:29 AM MST
*
* Modified : $Date$ UTC
*
* Revision : $Revision$
*
* @author Trenton D. Adams
*/
public class StatusBar extends Textbox implements AfterCompose, IStatusBar
{
@SuppressWarnings({"ConstantNamingConvention"})
private static final Logger logger = Logger.getLogger(StatusBar.class);
public StatusBar()
{
}
public void afterCompose()
{
logger.info("status bar running as " + getId());
// TODO find a better method, getFellow() didn't work
final Session session = getDesktop().getSession();
session.setAttribute("status", this);
}
public void setStatus(final String statusText, final int timePeriod)
{
setStatus(statusText);
// the timer must be in the page, or the page is inactive and this
// call would never happen?
final Timer timer = (Timer) getFellow("statusTimer");
timer.setDelay(timePeriod * 1000);
timer.start();
}
public void setStatus(final String statusText)
{
setText(statusText);
}
public String getStatus()
{
return getText();
}
}
The following code must be used inside of a ZK event handler, such as one that comes from the click of a button, or a mouse over event, etc. Basically, all we’re doing is grabbing the status bar object from the session, and request the status be set. That’s all there is to it.
final Session session = getDesktop().getSession();
final IStatusBar statusBar = (IStatusBar) session.getAttribute("status");
statusBar.setStatus("Transaction Posted", 5);
I’m attaching a screen shot of the results of my status bar.
]]>
The first thing to do is to configure ZK to handle your unhandled exceptions, and target a ZUL page for rendering. This is done through the zk.xml file.
java.lang.Throwable
/error.zul
The second thing to do is create an error.zul page, which I pulled mostly from the ZK Developers Guide. I added the bit where I include my error.jsp file.
I basically pulled this error page from the standard error page that I normally use, and tailored it a bit.
<%@ page import="java.io.PrintWriter" %>
<%@ page import="java.io.StringWriter" %>
<%@ page import="com.example.util.Util" %>
<%@ page isErrorPage="true" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="ce" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib uri="http://jakarta.apache.org/taglibs/string-1.1" prefix="st" %>
An error has occured. If you believe this error is a system problem, and you
continue to get this error, please contact a special person via
email
">
<%
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
%>
<%=sw.toString()%>
Message : <%=exception.getMessage()%>
Exception : <%=exception.getClass().getName()%>
Stack : ${fn:escapeXml(exceptionStack)}
And, we have the result; it looks quite nice. If debug were enabled, it would have my stack trace displayed in a wrap-able fixed width font ; providing much easier diagnostics during development.
]]>
In the case of existing web applications that make heavy use of JSP for display, ZK can be easily integrated into specific controls on the page, such as drop downs, text boxes, etc, to provide AJAX for that individual component. The component is then submitted using the normal GET/POST method. You can thereby provide AJAX auto complete, or dynamic load functionality to your heart’s content, without affecting the normal working of the page. If your goal is to move toward entirely using AJAX for that application, you can easily do it incrementally, so as not to have an overly complex and long development cycle. Just drop some of the ZUL includes directly into your JSP, write some ZSCRIPT or Java component/event classes, handle the processing, and you have yourself some event driven AJAX code.
ZUL files are essentially a derivative of XUL, defined for the purpose of AJAX handling in ZK. You define your GUI layout in XML, and write code to handle each component as you choose.
One of many great benefits of ZK, is that it has many different ZUL components for the normal input text box. You can make your text box a decimal box, a date box, int box, double box, etc, and AJAX will handle data validation for you, and wrapping it in the appropriate wrapper class such as BigDecimal, Integer, Double, etc. In fact, in many cases, it won’t even let you type in a character that is not allowed. For example, if you have specified a number box of some type, such as decimal, int, double, etc, it won’t even let you type an alpha character. You can also place constraints on your inputs such as “no zero“, or “no negative“, which would indicate that the box cannot accept a negative or zero input, but could be empty.
Here is a quick example of a decimalbox in action. I’ll give two examples. The first one has an implementing Java class, which you simply write a class that extends Decimalbox. The implementing class is just below that on the page. The second one achieves the exact same thing, but is not quite as flexible, for the simple reason that it’s not as dynamic, because I you cannot go instantiating it from code.
package com.example.common;
import com.example.util.CVUtil;
import org.apache.log4j.Logger;
import org.zkoss.zk.ui.ext.AfterCompose;
import org.zkoss.zul.Decimalbox;
public class LedgerDecimalBox extends Decimalbox implements AfterCompose
{
private static Logger logger = Logger.getLogger(LedgerDecimalBox.class);
public LedgerDecimalBox()
{ // load configured options
setFormat(Util.getStringItem("/inputs/decimalbox/format"));
setConstraint(Util.getStringItem("/inputs/decimalbox/constraints"));
setScale(2);
}
public void afterCompose()
{
logger.info("id: " + getId());
}
}
In all honesty, ZK seems to be absolutely amazing, very rich, very fast, very flexible, etc. In my opinion, it is probably THE BEST AJAX framework on the planet, by a LONG shot.
You can totally expect me to write more on ZK, as I learn the framework, and blog about various ways of doing things.
]]>