I am a computer engineer having many years of experience in commercial projects for various sectors like education, insurance, hardware, tourism, finance, IT, and telecommunications. Erdinç has posted 7 posts at DZone. View Full User Profile

An Event Mechanism for the Wicket Framework

03.29.2010
| 10819 views |
  • submit to reddit

The event system I will discuss about here  is based on the event handling mechanism used in Buoy Swing library (http://buoy.sourceforge.net). It is an excellent library to develop Swing applications. I modified the event handling code of buoy library in order to provide generics support and wicket integration.

Java has no mechanism corresponding to events and delegates in C#. Instead of them, we have to use interfaces and anonymous classes to be informed by component events; however, this leads to the proliferation of classes and makes it hard to read the source code.  This novel event system simplifies event handling, which implies clear separation between components and their clients.

I will develop a login page as an example to explain the concept.  This page includes a reusable component called “LoginComponent”.  The task of this component is to check if the user  types the correct username and password.  After checking the user input, the reusable component will send events indicating login status. If the user logs in successfully,  “success” field of event is set to “true”, otherwise it is set to “false”.  The “LoginPage” including the “LoginComponent” receives login events and acts accordingly. If login fails, it displays an error message; otherwise, it redirects the user to another page.

I am listing the code below. 

LoginPage.html

<html><head>
<title> Login page </title>
</head>
<body>
<table>
<tr>
<td>
<div wicket:id="feedbackPanel">
</div>
</td>
</tr>

<tr>
<td>
<div wicket:id="loginPanel">
</div>
</td>
</tr>
</table>

</body>

</html>


LoginPage.java

public class LoginPage extends EasyPage {

public LoginPage() {
add(new FeedbackPanel("feedbackPanel"));

LoginComponent loginComponent = new LoginComponent("loginPanel");
add(loginComponent);


loginComponent.addEventLink(LoginEvent.class, this, "onLogin");
}

@SuppressWarnings("unused")
private void onLogin(LoginEvent event) {
if ( event.isSuccess() ) {
// redirect to another page
info("Login successful");
}else {
error("Login failed");
}
}
}


Code listing for “LoginComponent”  is below;

LoginComponent.html

<wicket:panel>
<form wicket:id="form">
<table>
<tr>
<td> User name </td>
<td> <input type="text" wicket:id="txtUsername"> </td>
</tr>

<tr>
<td> Password </td>
<td> <input type="password"" wicket:id="txtPassword"> </td>
</tr>

<tr>
<td> </td>
<td> <button type="submit" wicket:id="btnLogin"> Login </button> </td>
</tr>

</table>
</form>

</wicket:panel>


LoginComponent.java

public class LoginComponent extends EasyPanel {

@EasyWicket(id="form")
Form<Void> form;

@EasyWicket(id="form.txtUsername", value="username", required=true)
TextField<String>txtUsername;

@EasyWicket(id="form.txtPassword", value="password", required=true)
PasswordTextField txtPassword;

@EasyWicket(id="form.btnLogin", action="actionLogin")
Button btnLogin;

String username, password;

private static Logger logger = LoggerFactory.getLogger(LoginComponent.class);

public LoginComponent(String id) {
super(id);

}

public void actionLogin() {
if ( logger.isInfoEnabled() ) {
logger.info("username=" + username + " password=" + password);
}


LoginEvent event = new LoginEvent(this);
event.setSuccess(System.currentTimeMillis() % 2 == 0);
dispatchEvent(event);
}

}

Finally, source code of “LoginEvent”

LoginEvent.java

public class LoginEvent extends WicketEvent {

private boolean success;


public LoginEvent(Component source) {
super(source);
}

public void setSuccess(boolean success) {
this.success = success;
}

public boolean isSuccess() {
return success;
}
}


To receive events, event listener must be registered as in “LoginPage.java”

loginComponent.addEventLink(LoginEvent.class, this, "onLogin");


This code snippet says, “this” is registered as “LoginEvent” listener and “onLogin” method must be called when “LoginEvent” is triggered.
To support this type of behavior, “LoginComponent” must use the  event dispatcher as in following code:

From JavaComponent.java

LoginEvent event = new LoginEvent(this);
event.setSuccess(System.currentTimeMillis() % 2 == 0);

dispatchEvent(event);

This code creates a login event, sets the “success” field according to the login result, and sends event to the listeners of the event.
As you see from the source code,  user interface elements are annotated with “@EasyWicket” annotation. This annotation comes from EasyWicket library I discussed about in my previous article.  I  also incorporated the event handling code into the EasyWicket library. You can reach the codes here.

Published at DZone with permission of its author, Erdinç Kocaman.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Hauke Ingmar Schmidt replied on Tue, 2010/03/30 - 4:54am

String bindings for method names is a very bad idea IMHO. Just to avoid an anonymous class that is easily written? And that can be refactored? Notice the @SuppressWarnings("unused") in the example code. This way the coupling is tighter. I like C#'s delegates. But this is not a good substitute.

Erdinç Kocaman replied on Tue, 2010/03/30 - 6:08am

Pros
- Pragmatic approach
- Event classes help to identify who calls who.
- Not using anonymous classes result in cleaner code.

Cons
- String binding. Damage can be reduced if you keep the code structured.
- Difficulty in refactoring. But we live in scripting era :)

Hauke Ingmar Schmidt replied on Tue, 2010/03/30 - 7:15am

I don't agree with any of your "Pro" points. What should "Pragmatic approach" even mean? Using another library with yet another syntax instead of the well-established way to do in Java (instantiating an anonymous class or overriding a method) is pragmatic? I don't agree that the code is cleaner, in my opinion it is worse than the standard way to do this. Java is not a scripting language, for good reasons. Trying to introduce scripting features will create a hybrid with the worst from both worlds (like in this example - Java's verbosity, scripting's unmaintainability). If you want scripting, then there are enough scripting languages running at the JVM and allowing the use of Java libraries like Wicket.

Erdinç Kocaman replied on Tue, 2010/03/30 - 7:45am

Actually, I don't like scripting languages much but Java needs some flexibility. This approach may not be suitable for every situation. You may not use this approach for business logic but it can be useful for gui programming.

Tim Boudreau replied on Tue, 2010/03/30 - 8:51pm

Wow! The entire *point* of Wicket is to allow people to write straightforward Java code with clean separation of logic and markup, and no magic plumbing. And in one fell swoop, you've managed to defeat both of those things! I'm perversely impressed...

I suspect that, since Wicket components are serializable, and serialization is used to implement clustering (so you want to minimize session size to minimize the bytes that need to be sent over the wire), that a strategy of adding ad-hoc listeners to components (where the entire object graph of the listener object will need to be serialized with the component) could easily lead to the same kind of listener-memory-leak issues Swing has, with a serious negative impact on performance in a clustered environment.

What I see here is a solution - but there isn't really any mention of what problem you're trying to solve. Who will create these listeners? Is the only problem that this solves a distaste for inner classes? A desire to have generic multicast support for form submission?

Seems kind of like turning Wicket into JSF...

Erdinç Kocaman replied on Wed, 2010/03/31 - 2:04am

What i'am trying to do;

  • Increasing readability - important for me
  • No custom listener classes and listener call mechanism - just use a builtin class or create a custom event class and use existing event handling mechanism for this. You can easily add new information to event but it may be more difficult to change interface signature. Principle : It is good idea to abstract changing parts. Events are good abstraction for information passing.
  • Faster coding - I am tired of writing new TextField(...) or implement an interface just to receive a notification from a component, it is pointless for me. These don't add any value to the application.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.