Skip to main content

RJS API For Java

Posted by jhook on October 12, 2006 at 12:47 PM PDT

If you take a look at GWT (Google Web Toolkit), you are dealing with a subset Java API to accomodate JavaScript execution on the client. While this does tend to push more execution on the client, it does limit your ability to coordinate traditional transactions and APIs that you are used to with the full JEE stack. What if you were to flip the tables on GWT and use server-side Java to generate JavaScript to execute on the client?

With this approach, an AJAX request is made to the server, and instead of responding with data, you respond back with JavaScript code to execute which manipulate the screen or refresh different parts of the screen. JavaScript generation is supplemented by 'native' APIs to Java.

While I'm sure there were others doing this long before, the Rails community has made the approach quite popular with RJS. You can read a bit on Rails solution here.

As part of our JavaOne demo last year, I included an example of this with JSF, but the API looked like:

ClientWriter writer = ...;
String errorMsg = "This is an Error to push to the client.";
writer.select('myDiv', Element.hide());
writer.select('errorMsg', Element.setHtml(errorMsg), Element.show(), Element.highlight());

This would generate the following JavaScript in the response, which would use JavaScript libraries such as Prototype or Scriptaculous.

<script type="text/javascript">
Element.hide($('myDiv'));
$('errorMsg').innerHTML = 'This is an Error to push ....';
Element.show($('errorMsg'));
Effects.highlight($('errorMsg'));
</script>

This JavaScript would then be evaluated on the client when the AJAX response was received, just like any other local JavaScript function, except we've generated the logic on the fly, using the full JEE stack on the server.

While we can identify elements in the browser DOM by ID, JSF has a (complex) clientId system for identifying elements in the DOM. We can simplify this by adding methods to the ClientWriter which just take in a UIComponent as your selection criteria:

<h:inputText value="#{bean.name}" binding="#{bean.nameInput}"/>
<h:commandButton action="#{bean.action}"/>

// MyBean.java
private UIInput nameInput;
public void setNameInput(UIInput input) {
  this.nameInput = input;
}

public String action() {
   ....
   if (loginFailed) {
       ClientWriter writer = ....;
       writer.select(this.nameInput, Effect.highlight());
   }
}

You can see a live prototype of this in the context of constructing a cart and updating various parts of the page here.

Some of the logical pros of this approach to server-side generation of JavaScript with AJAX are:

  1. Keep data models on the server
  2. Prevents unecessary data marshalling
  3. Minimizes JavaScript development required on the client (instead of receiving XML/data, interpreting, and then updating the DOM)
  4. Full JEE stack available, such as working within (Hibernate/EJB3) transactions
  5. Can implement on top of familiar, existing frameworks such as Struts, WebWork, or JSF

So what does the API look like in implementation?

// ClientWriter.java
public ClientWriter select(String id, Script... s) throws IOException {
    if (s == null) return this;
    String var = this.getBuffer().append("${'").append(id).append("')").toString();
    for (Script i : s) {
        i.write(var, this);
    }
    return this;
}

// ClientWriter.java (for CSS-like selectors from Prototype.js)
public ClientWriter select(String selector, Script... s) throws IOException {
    if (s == null || selector == null) return this;
    this.writer.write(this.getBuffer()
        .append("$$('")
        .append(selector)
        .append("').each(function(e){").toString());
    for (Script i : s) {
        i.write("e", this);
    }
    this.writer.write("});");
    return this;
}

// Script.java
public interface Script {
    public void write(String variable, ClientWriter writer) throws IOException;
}

// Element.java
private final static Script Show = new Script() {
    public void write(String variable, ClientWriter writer)
            throws IOException {
        writer.write(variable);
        writer.write(".style.display='';");
    }
};
private final static Script Hide = new Script() {
    public void write(String variable, ClientWriter writer)
            throws IOException {
        writer.write(variable);
        writer.write(".style.display='none';");
    }
};
public static Script toggle(boolean show) {
    return show ? Show : Hide;
}

I'd be interested if others have ideas on a better method of handling/coordinating generation of JavaScript code in a Java API? As you add more JavaScript libraries, you can add additional Script singletons to generate the necessary code-- I guess similar to what would be required to code custom GWT components with their method of handling 'native' calls.

Related Topics >>