Skip to main content

Gooey Event Proxy

Posted by evanx on July 21, 2008 at 1:02 PM PDT

In the Gooey MvnC prequel, we consider an MVC-type separation for programming a Swing UI panel, and convention-over-configuration to automate events, threading and beans binding.

Now as an illustration of the above intent, we implement a minimalistic helper class to create dynamic proxies for event listeners wired to event handler methods in our controller.

Gooey Event Proxy: A part of "Gooey Beans, a trilogy in 42 parts"

In our controller, we implement event handler methods as follows.

public class LoginController implements GEventController {
   LoginView view = new LoginView();
   LoginModel model = new LoginModel();
   GEventHelper eventHelper = new GEventHelper(this, view, model);
   ...  
   void usernameFocusLost() {
      ... // check that username is valid
   }
  
   void cancelActionPerformed() {
      ... // handle cancel button pressed
   }  
  
   SwingWorker okActionPerformed() {
      ... // worker for OK button pressed to login
   }     
}

Our helper creates dynamic proxies as event listeners registered on our components, as follows.

public class GEventHelper {
    protected GEventController controller;
    protected Map components = new HashMap();
    protected Map bindings = new HashMap();

    public GEventHelper(GEventController controller, Component view, GBean model) {
        this.controller = controller;
        initComponents(view); // populate components map
        bind(model); // create bindings/listeners
        connectController();
    }
    ...
    public void connectController() {
        for (Component component : components.values()) {
            if (GInputComponentAdapterFactory.isInputComponent(component)) {
                GInputComponentAdapterFactory.create(component).addActionListener(
                        (ActionListener) createListenerProxy(component,
                        ActionListener.class));
                GInputComponentAdapterFactory.create(component).addFocusListener(
                        (FocusListener) createListenerProxy(component,
                        FocusListener.class));
                ... // other events
            }
        }
    }
   
    protected Object createListenerProxy(Component source, Class listenerType) {
        return Proxy.newProxyInstance(
                source.getClass().getClassLoader(),
                new Class[]{listenerType},
                new GEventProxy(controller, source));
    }
    ...
}

where our GEventProxy below will invoke methods in our controller e.g. usernameFocusLost() when focusLost() is invoked on a proxy listener registered on a component named "username".

public class GEventProxy implements InvocationHandler {
   ...
   protected void invoke(Method method, Object[] args) {
        for (Method eventMethod : eventMethods.values()) {
            if (eventMethod.getName().equalsIgnoreCase(
                    component.getName() + method.getName())) {
                invokeHandler(eventMethod, args);
                break;
            }
        }
    }
}

Resources

https://code.google.com/p/vellum/ - where i will collate these articles and their code.

Related Topics >>