User defined events and MVC

9 views
Skip to first unread message

bly...@googlemail.com

unread,
Nov 4, 2007, 12:55:45 PM11/4/07
to Google Web Toolkit
Hi all,

using the MVC pattern for the client side faced me the problem that I
could not find an event system in GWT. If I have a modell e.g. for a
login session, I cannot raise my own events like loginSucceded to
update my views. So I decided to implement the observer pattern and my
own events managed by an EventManager class. The EventManager class
should be able to attach listeners to a certain event type but I
discovered, that the jse Object.getClass() function is not present.

According to some posts I found out that I could use
EventManager.attach(GWT.getTypeName(new Event()), MyListener) to
implement this behaviour but it does not feel right to create a new
event just to register a listener.

Is there any other way to get the class type without creating an
instance? Maybe you know another way to use user-defined events?

regards,
Dominik

Peter Blazejewicz

unread,
Nov 4, 2007, 11:08:31 PM11/4/07
to Google Web Toolkit
hi Dominik,

why you need getClass() and all that stuff?

// EVENT

package com.mycompany.project.client;

import java.util.EventObject;

import com.google.gwt.core.client.GWT;

public abstract class CustomEventObject extends EventObject {
protected String type;
public Object data;

public CustomEventObject(Object source) {
super(source);
}

public String getType() {
return type;
}

/* @Ovveride */
public Object getSource() {
return super.getSource();
}

/* @Override */
public String toString() {
return "Event: " + GWT.getTypeName(this) + " type: " + getType()
+ " target: " + GWT.getTypeName(getSource());
}

}


// LISTENER

package com.mycompany.project.client;

import java.util.EventListener;

public interface CustomEventListener extends EventListener {
public void onEvent(CustomEventObject event);
}


// EVENT MANAGER

package com.mycompany.project.client;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;

public class EventManager {
private static final EventManager instance = new EventManager();
private static final Map listeners = new HashMap();

public static EventManager getInstance() {
return instance;
}

public void addEventListener(String type, CustomEventListener
listener) {
List typeListeners = null;
if (listeners.containsKey(type) == false) {
listeners.put(type, new EventListenersCollection());
}
typeListeners = (List) listeners.get(type);
typeListeners.add(listener);
}

public void removeEventListener(String type, CustomEventListener
listener) {
List typeListeners = null;
if (listeners.containsKey(type) == true) {
typeListeners = (List) listeners.get(type);
typeListeners.remove(listener);
}
}

public void dispatchEvent(final CustomEventObject event) {
if (listeners.containsKey(event.getType()) == true) {
final EventListenersCollection typeListeners =
(EventListenersCollection) listeners
.get(event.getType());
DeferredCommand.addCommand(new Command() {
public void execute() {
typeListeners.onEvent(event);
}
});

}
}

static class EventListenersCollection extends ArrayList {
private static final long serialVersionUID = 3425680778337656685L;

public void onEvent(CustomEventObject event) {
for (Iterator it = iterator(); it.hasNext();) {
CustomEventListener listener = (CustomEventListener) it.next();
listener.onEvent(event);
}
}
}
}


///

now example concrete events/listeners;

package com.mycompany.project.client;

public class LoginEvent extends CustomEventObject {
private static final long serialVersionUID = -6066074958503856659L;
public static final String LOGIN_EVENT = "loginEvent";
private String login;

public LoginEvent(Object source, String login) {
super(source);
this.type = LOGIN_EVENT;
this.login = login;
}

public String getLogin() {
return login;
}

}

package com.mycompany.project.client;

import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;

public class LoginEventListener implements CustomEventListener {

public void onEvent(CustomEventObject event) {
System.out.println(event.toString());
LoginEvent loginEvent = (LoginEvent) event;
final Button btn = (Button) loginEvent.getSource();
final String btnLabel = btn.getText();
btn.setEnabled(false);
btn.setText("Validating...");
final String userLogin = loginEvent.getLogin();
Timer t = new Timer() {
public void run() {
btn.setEnabled(true);
btn.setText(btnLabel);
Window.alert("Connected as: " + userLogin);
}
};
t.schedule(2000);

}

}


package com.mycompany.project.client;

public class MyCustomEvent extends CustomEventObject {
private static final long serialVersionUID = 4254389761616981854L;
public static final String CUSTOM_ON_LOAD_EVENT =
"customOnLoadEvent";

public static final String CUSTOM_LOADED_EVENT = "customLoadedEvent";
public static final String CUSTOM_BROWSER_RESIZED_EVENT =
"customBrowserResizedEvent";

public MyCustomEvent(String type, Object source) {
super(source);
this.type = type;
}

}


example usage:

package com.mycompany.project.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class ExampleModule implements EntryPoint {
OnLoadListener onLoadListener;

public ExampleModule() {
onLoadListener = new OnLoadListener() {
public void onEvent(CustomEventObject event) {
super.onEvent(event);
Window.alert("Loaded");
}
};

EventManager.getInstance().addEventListener(OnLoadEvent.ON_LOAD_EVENT,
onLoadListener);
}

public void onModuleLoad() {
RootPanel rootPanel = RootPanel.get();

final VerticalPanel submitForm = new VerticalPanel();
rootPanel.add(submitForm, 5, 5);

final HorizontalPanel horizontalPanel = new HorizontalPanel();
submitForm.add(horizontalPanel);

horizontalPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);

final Label loginLabel = new Label("Login:");
horizontalPanel.add(loginLabel);

final TextBox loginInput = new TextBox();
horizontalPanel.add(loginInput);
loginInput.setWidth("150px");
loginInput.setName("login");

final Button loginButton = new Button();
submitForm.add(loginButton);
loginButton.setWidth("150px");
submitForm.setCellHorizontalAlignment(loginButton,
HasHorizontalAlignment.ALIGN_RIGHT);
loginButton.addClickListener(new ClickListener() {
public void onClick(final Widget sender) {
if (loginInput.getText() != null
&& loginInput.getText().trim().length() > 0) {
// DISPATCH LOGIN EVENT
EventManager.getInstance().dispatchEvent(
new LoginEvent(sender, loginInput.getText()));
}
}
});
loginButton.setText("LOGIN");
//
EventManager manager = EventManager.getInstance();
// ADD LOGIN EVENT LISTENER
manager.addEventListener(LoginEvent.LOGIN_EVENT,
new LoginEventListener());
//
CustomEventListener customListener = new CustomEventListener() {
public void onEvent(CustomEventObject event) {
System.out.println(event.toString());
System.out.println("Hello from " + event.data.toString());
};
};
// ADD CUSTOM EVENT
manager.addEventListener(MyCustomEvent.CUSTOM_LOADED_EVENT,
customListener);
CustomEventObject event = new OnLoadEvent(this);
CustomEventObject customEvent = new MyCustomEvent(
MyCustomEvent.CUSTOM_LOADED_EVENT, this);
customEvent.data = "Peter";
//
manager.dispatchEvent(customEvent);
manager.dispatchEvent(event);
}

/* @Override */
public String toString() {
return GWT.getTypeName(this);
}
}


// OUTPUT:

Event: com.mycompany.project.client.MyCustomEvent type:
customLoadedEvent target: com.mycompany.project.client.ExampleModule
Hello from Peter
Event: com.mycompany.project.client.OnLoadEvent type: onLoadEvent
target: com.mycompany.project.client.ExampleModule
Event: com.mycompany.project.client.LoginEvent type: loginEvent
target: com.google.gwt.user.client.ui.Button

and handling from LoginListener should be delegated to e.g. command
pattern,

regards,
Peter

On Nov 4, 6:55 pm, "blys...@googlemail.com" <blys...@googlemail.com>
wrote:

bly...@googlemail.com

unread,
Nov 5, 2007, 7:45:48 AM11/5/07
to Google Web Toolkit
Czesc Peter,

thanks for the nice example, I definitely learned a few things of it!
The point is, I wanted to map the listeners to the event class type,
so there would be no need to store the type in a string like you did
in e.g. LOGIN_EVENT. Imagine you rename your EventClass, you might
have to rename the variable names and the strings as well to make it
fit a new naming.

On the other hand, your solution is more flexible and powerful in
general usage.

It would be nice to see other implementations on this topic, just to
see different ideas and perspectives.

pozdrawiam
Dominik

On Nov 5, 5:08 am, Peter Blazejewicz <peter.blazejew...@gmail.com>
wrote:

Peter Blazejewicz

unread,
Nov 5, 2007, 5:14:04 PM11/5/07
to Google Web Toolkit
hi Dominik,

the idea of haveing "type" field is that given instance of Event
object can be used for different purposes,
say I don't need to declare only single type,
if I make it not strictly realted to existing java evnet objects and
declare it that way:

package com.mycompany.project.client;

import com.google.gwt.core.client.GWT;

public class GlaxEvent {
private String type;
public Object data;

public GlaxEvent(String type) {
this.type = type;
}

public void dispatch() {
GlaxEventManager.getInstance().dispatchEvent(this);
}

public String getType() {
return type;
}

/* @Override */
public String toString() {
return "Event: " + GWT.getTypeName(this) + " type: " + getType();
}

}

(supposing there is a "Glax" framework"),
then I can define as many types for given event as I want:

event = new GlaxEvent(ON_LOAD_EVENT);
manager.addEventListener(ON_LOAD_EVENT, myListener);
event = new GlaxEVent(ON_FOCUS_EVENT);
manager.addEventListener(ON_FOCUS_EVENT, myListener);
manager.addEventListener(ON_FOCUS_EVENT, myOtherListener);

and even implement concrete types that way:

package com.mycompany.project.client;

public class LoginEvent extends GlaxEvent {


public static final String LOGIN_EVENT = "loginEvent";

public String username;
public String password;

public LoginEvent(String username, String password) {
super(LOGIN_EVENT);
this.username = username;
this.password = password;
}

}

There are IDE tools that allow to avoid issues with class name
refactoring,
there are JUnit tests that allow to avoid issues with class fields
refactoring,

I would be glad to see getClass() in jre emulation but I can live
without that,

regards,
Peter

On Nov 5, 1:45 pm, "blys...@googlemail.com" <blys...@googlemail.com>
wrote:

> ...
>
> read more »

Sumit Chandel

unread,
Nov 8, 2007, 10:45:15 PM11/8/07
to Google-We...@googlegroups.com
Hi Dominik,

Another more simplistic approach that you can take to solve this problem is to implement an event-driven architecture much like the one used for the SuggestBox widget. That is:

1) Define your Event class (much like the code already shown in Peter's previous posts). Using the SuggestBox widget as an example, this would be the SuggestEvent. In your specific case this would be LoginEvent.

2) Define your Event Listener interface and have your custom listeners implement it (again, much like what has already been described in the code snippets above). For the SuggestBox, this would be the SuggestionHandler interface. For the login event, this would be LoginHandler.

3) Define an interface to denote classes that are capable of firing the specific event. Any type that is capable of firing the specific event must also be responsible for adding and removing event listeners, so the signature methods you would define for the a FiresLoginEvent interface would be addLoginHandler(LoginHandler handler) and removeLoginHandler(LoginHandler). For the SuggestBox, this interface is the FiresSuggestionEvents interface.

Check out the SuggestBox and its event system components for an example implementation:

http://google-web-toolkit.googlecode.com/svn/javadoc/1.4/com/google/gwt/user/client/ui/SuggestBox.html

With this architecture, each component that is able to fire a specific event is also responsible for maintaining the list of event listeners who care about the event, which I think is what you're looking for.

Hope that helps,
-Sumit Chandel

On Nov 5, 2007 2:14 PM, Peter Blazejewicz <peter.bl...@gmail.com> wrote:

hi Dominik,

the idea of haveing "type" field is that given instance of Event
object can be used for different purposes,
say I don't need to declare only single type,
if I make it not strictly realted to existing java evnet objects and
declare it that way:

package com.mycompany.project.client;

import com.google.gwt.core.client.GWT;

public class GlaxEvent {
       private String type;
       public Object data;

       public GlaxEvent(String type) {
               this.type = type;
       }

       public void dispatch() {
               GlaxEventManager.getInstance().dispatchEvent(this);
       }

       public String getType() {
               return type;
       }

       /* @Override */
       public String toString() {
               return "Event: " + GWT.getTypeName (this) + " type: " + getType();
> >                                 if ( loginInput.getText() != null

> >                                                 && loginInput.getText().trim().length() > 0) {
> >                                         // DISPATCH LOGIN EVENT
> >                                         EventManager.getInstance().dispatchEvent(
> >                                                         new LoginEvent(sender, loginInput.getText()));
> >                                 }
> >                         }
> >                 });
> >                 loginButton.setText("LOGIN");
> >                 //
> >                 EventManager manager = EventManager.getInstance();
> >                 // ADD LOGIN EVENT LISTENER
> >                 manager.addEventListener(LoginEvent.LOGIN_EVENT,
> >                                 new LoginEventListener());
> >                 //
> >                 CustomEventListener customListener = new CustomEventListener() {
>
> ...
>
> read more >>



Reply all
Reply to author
Forward
0 new messages