Server-Side Java Sync Peers

To make use of your own custom client-side JavaScript components from within a server-side Java Echo application, you will need to write create a new server-side Component class and a ComponentSynchronizePeer The Component object will represent the objects state, while the ComponentSynchronizePeer will be responsible for transferring that state information from server to client (and vice-versa).

Creating the Component

To create a component that will provide a custom synchronization peer, simply define a Java class that extends Component directly:

package example;

import nextapp.echo.app.Component;
import nextapp.echo.app.FillImage;

public class SpinButton extends Component {

    public static final String PROPERTY_BACKGROUND_IMAGE = "backgroundImage";
    public static final String VALUE_CHANGED_PROPERTY = "value";
    
    private int value;
    
    public SpinButton() {
        super();
    }
    
    public FillImage getBackgroundImage() {
        return (FillImage) get(PROPERTY_BACKGROUND_IMAGE);
    }
    
    public int getValue() {
        return value;
    }
    
    public void setBackgroundImage(FillImage newValue) {
        set(PROPERTY_BACKGROUND_IMAGE, newValue);
    }
    
    public void setValue(int newValue) {
        int oldValue = value;
        value = newValue;
        firePropertyChange(VALUE_CHANGED_PROPERTY, new Integer(oldValue), new Integer(newValue));
    }
}

The above code shows a Component implementation for the SpinButton example. A getter and setter method has been added for each of its two properties, value and backgroundImage.

The first property, backgroundImage is known as a "style property", in that it will be stored in the component's internal style. Such properties are retrieved and stored using the Component's get() and set() methods. Style properties should be used in most cases, such that values may be specified within a Style or StyleSheet object rather than having to be specified for each Component instance..

The second property, value is not a style property. A property like "value" has no business being overridden by a Style or StyleSheet, and could conceivably be implemented later using a proper model object (as is done with DocumentModel for Echo's various TextComponent implementations). Non-style or "model" properties are stored in instance variables.

Creating the Peer

Component synchronization peers implement the nextapp.echo.webcontainer.ComponentSynchronizePeer interface. This interface has quite a number of methods in it to control every detail about how a component is synchronized between the client and server. A helper class is provided that offers default implementations for all but a minimum number of methods that must be implemented by the developer. Generally speaking, you'll want to extend this AbstractComponentSynchronizePeer class and override any methods where you require non-default behavior.

To create a peer for the SpinButton component, we'll extend AbstractComponentSynchronizePeer Default implementations of all methods of ComponentSynchronizePeer are provided by this class, save the following two:

  • getClientComponentType(): specifies the client-side component type name.
  • getComponentClass: Specifies the Java component class.

Additionally we'll need to have the client download the JavaScript module that provides the client-side component. To do this, we'll create a static JavaScriptService instance containing the JavaScript code, and then override the init() method of AbstractComponentSynchronizePeer to allow this service to be rendered the first time such a component is used.

In Echo3, most of the Java-to-XML-to-JavaScript serialization work is handled automatically. You only need to inform Echo of a few key pieces of information about your component. In the simplest case, you only need to describe in your peer what the client component type is, what the corresponding server component type is, and what JavaScript module(s) need to be loaded on the client.

The following code shows our first go at a ComponentSynchronizePeer implementation:

package example;

import nextapp.echo.app.Component;
import nextapp.echo.app.util.Context;
import nextapp.echo.webcontainer.AbstractComponentSynchronizePeer;
import nextapp.echo.webcontainer.ServerMessage;
import nextapp.echo.webcontainer.Service;
import nextapp.echo.webcontainer.WebContainerServlet;
import nextapp.echo.webcontainer.service.JavaScriptService;

public class SpinButtonPeer extends AbstractComponentSynchronizePeer {

    // Create a JavaScriptService containing the SpinButton JavaScript code.
    private static final Service JS_SERVICE = JavaScriptService.forResource(
            "Example.SpinButton", "example/SpinButton.js");

    static {
        // Register JavaScriptService with the global service registry.
        WebContainerServlet.getServiceRegistry().add(JS_SERVICE);
    }

    public String getClientComponentType(boolean shortType) {
        // Return client-side component type name.
        return "Example.SpinButton";
    }

    public Class getComponentClass() {
        // Return server-side Java class.
        return SpinButton.class;
    }

    public void init(Context context, Component component) {
        super.init(context, component);
        // Obtain outgoing 'ServerMessage' for initial render.
        ServerMessage serverMessage = (ServerMessage) context
                .get(ServerMessage.class);
        // Add SpinButton JavaScript library to client.
        serverMessage.addLibrary(JS_SERVICE.getId());
    }
}

The above code does not yet provide any capability to synchronize the non-style property value defined in the our Component class. This will be discussed in a later section.