STJS and jQuery

We were already using jQuery when we decided to build STJS. GWT handles the cross-browser issue by generating separate code for each target platform. We believe that it’s easier to leave this task to external libraries such as jQuery.

To be able to use jQuery in Java (with STJS) you need to have what we call a “bridge”. This is a Java code composed mainly of Java interfaces and empty static methods that mimic the Javascript functions. The static methods have no body or throw UnsupportedMethodException.

The jQuery core bridge

The bridge to the jQuery core functionality is found in the org.stjs.javascript.jquery.JQueryCore interface. Here is an excerpt from this interface.

@SyntheticType
public interface JQueryCore<FullJQuery extends JQueryCore<?>> {
 public FullJQuery addClass(String className);

public boolean hasClass(String className);

public FullJQuery removeClass();

...

The interface is annotated with @SyntethicType as this bridged type does not exist in the corresponding Javascript code (no JS function called JQueryCore).

One of the most handy features of jQuery is its ability to chain methods together, so it was important for us to represent this feature in java. So as you can see, the most of the methods return object of the type FullJQuery, which is a generic parameter that has to conform to the JQueryCore interface. This element is the key point that allows extending jQuery via plugins, as I explain later.

To make the usage of jQuery API more precised in Java, we tried to propose methods with the signature as strict as possible – so we tried to avoid Object or Object … parameters wherever possible. This was quite a challenging task  – and we didn’t get it quite right from the first try 🙂 as the library’s documentation is not always precise regarding the parameters’ types. Here is a short excerpt:

public Object css(String propertyName);
public FullJQuery css(String propertyName, Object value);
public FullJQuery css(Map<String, ? extends Object> propertyMap);

Even though when you write regular (non bridge) Java classes for STJS overloading is forbidden, for clarity purposes, when you write bridges this is allowed. As you can imagine, in our case all the three methods refer to the SAME Javascript function, but where the overloading is handled internally by the function.

function( elem, name, value ) {
 return value !== undefined ?
 jQuery.style( elem, name, value ) :
 jQuery.css( elem, name );

Plugins

The problem is that jQuery has a design that might seem unfamiliar to most of Java developers. I don’t know of a Java library where your library’s plugins add methods to your classes! Most of the time,you have an API and accompanying SPI, such as javax.crypto.Cipher and javax.crypto.CipherSpi or javax.imageio and javax.imageio.spi, and the plugins are added by implementing the SPI, whereas clients still use the API. In fact the Java language itself is not yet ready for this type of “extension”. The concept is called “Extension Methods” and the C# implementation is the closest to this concept. The Java implementation that should arrive in Java 8 is not quite helpful for this type of design.

One of the issues of Javascript in general and of jQuery in particular is the heavy overloading of the methods (especially for plugins). The same method can be used for activating the plugin, setting options or more unexpected – call methods and return values. Even though we tried to “strongly-type” the interface by providing several method signatures for the different plugins, there is still the problem of the return type (and additional parameters) that changes depending on the first parameter called “method name”. Here is an example:

@SyntheticType
public interface Draggable<FullJQuery extends JQueryCore<?>> {
 public FullJQuery draggable();
 public FullJQuery draggable(DraggableOptions<FullJQuery> options);
 public FullJQuery draggable(String methodName);
 public Object draggable(String option, String optionName);
 public FullJQuery draggable(String option, DraggableOptions<FullJQuery> options);
 public FullJQuery draggable(String option, String optionName, Object value);
}

One of the problems we had when we used jQuery prior to STJS was that we never knew by heart what options a plugin offers or what parameters you can access in the event handler. That’s why, instead of proposing as a parameter a simple Map for the options we went further and we strongly-typed that part too. So here how it looks (partially) the options parameter and the ui object for the Draggable plugin:

@SyntheticType
abstract public class DraggableUI<FullJQuery extends JQueryCore<?>> {
 public FullJQuery helper;
 public Position position;
 public Position offset;
}

@SyntheticType
public class DraggableOptions<FullJQuery extends JQueryCore<?>> {
 public boolean disabled = false;
 public boolean addClasses = true;
 public Object appendTo = "parent";
 public String axis = "false";
 public String cancel = ":input,option";
 public String connectToSortable = "false";
...

The jQuery core and the UI plugins are all brought together in the interface JQueryAndPlugins:

@SyntheticType
public interface JQueryAndPlugins<FullJQuery extends JQueryAndPlugins<?>> extends JQueryCore<FullJQuery>, //
 JQueryUI<FullJQuery>,//
 Accordion<FullJQuery>,//
 AutoComplete<FullJQuery>,//
 Button<FullJQuery>,//
 Datepicker<FullJQuery>,//
 Dialog<FullJQuery>,//
 Draggable<FullJQuery>,//
 Droppable<FullJQuery>,//
 Progressbar<FullJQuery>,//
 Resizable<FullJQuery>,//
 Selectable<FullJQuery>,//
 Slider<FullJQuery>,//
 Sortable<FullJQuery>,//
 Tabs<FullJQuery>//
{
}

This interface is intended to be further subclassed by the users who wish to add other jQuery plugins. If you plan to use only the plugins listed above you can use the JQuery interface that is a shorter name for the previous interface:

@SyntheticType
public interface JQuery extends JQueryAndPlugins<JQuery> {//
}

Writing your own plugin bridge

It may happen that you need to add a new jQuery plugin in your code (let’s call it MyPlugin). Following the same rules as for the UI plugins you’d write the interface representing your plugin:

@SyntheticType
public interface MyPlugin<FullJQuery extends JQueryCore<?>> {
 public FullJQuery myplugin();
 public FullJQuery myplugin(MyPluginOptions<FullJQuery> options);
 public FullJQuery myplugin(String methodName);
 public Object myplugin(String option, String optionName);
 public FullJQuery myplugin(String option, String optionName, Object value);
}

Then, wherever you previously used the JQuery interface you should use the MyJQueryLib interface defined as follows:

public interface MyJQueryLib<FullJQuery extends MyJQueryLib<?>>
 extends MyPlugin<FullJQuery>,
 JQueryAndPlugins<FullJQuery>{
}

To make it easier (no need for cast) you can re-write the global $ method to return your new JQuery interface as it’s described in the next paragraph.

The global methods and object

There are basically two ways to access the jQuery functionality from Javascript: via the $ global object or via the $ global method (I assume the $.noConflict is not used).
The global object gives you access to data and methods not necessarily linked to a DOM node, like $.browser or $.inArray().
The global method $()  uses a selector to build a list of DOM elements to which subsequent calls will be applied.

In STJS, the global  jQuery method and object are defined in the class: org.stjs.javascript.jquery.GlobalJQuery. They are defined as follows:

abstract public class GlobalJQuery {

public static GlobalJQuery $;

public static <FullJQuery extends JQueryAndPlugins<?>> FullJQuery $(String path) {
 throw new UnsupportedOperationException();
 }

...

}

The GlobalJQuery object contains all the methods and fields that can be accessed with $. like $.browser and $.inArray.
As you can see, the $ method returns a jQuery wrapper object that provides you all the methods of  jQuery core and UI plugins. If you decide to add new plugin and if you want to access to the newly added methods without casting, you may need to define your own GlobalJQuery inheriting from the STJS one:

@GlobalScope
abstract public class MyGlobalJQuery extends GlobalJQuery {
 public static MyGlobalJQuery $;

public static MyJQuery $(String path) {
throw new UnsupportedOperationException();
 }

..

}

Even if you don’t add methods to your MyGlobalJQuery instance you still have to re-define the $ global object in order to be able to statically import in your code the $ object and method

import static my.package.MyGlobalJQuery.$;

I hope this article gave you some insight on the challenges we had implementing this bridge. It may hopefully serve you as a starting point for implementing a bridge to other similar Javascript libraries.

Next article will describe how to implement a jQuery widget using STJS.

Strongly-Typed Javascript

I had the idea of building a Java to Javascript generator in the summer of 2011 when I saw the time we spent on writing, debugging and fixing Javascript code compared to the similar task in Java. Let alone the very poor support from our preferred IDE – Eclipse. We obviously had a look at GWT at the time, but it looked way too complicated for what we needed. I asked around the people I knew who had an experience with GWT and unfortunately they confirmed my feeling. So I spoke to my old colleague and friend Eyal about the idea, and to my great surprise not only he thought the idea is worth something, but he agreed to help me developing it. So here how Strongly-Typed Javascript was born! The name itself was supposed to be temporary – let’s develop it and we’ll find a name later 🙂 But the acronym finally stuck, STJS being quite easy to remember.

Since we launched STJS, Google launched the Dart language. But we still believe that it’s not exactly the same direction. In fact Google themselves are not quite sure of how these two technologies fit together. And after I read the article http://www.javacodegeeks.com/2012/01/gwt-pros-and-cons.html I believe that other developers share our feeling that GWT maybe not be the final answer to web development for Java developers.

Three months after the first public release, we received quite encouraging messages from the developers who “dared” to use it. After the first article on TheServerSide, I believe more in-depth presentations of the technology may be of interest and a blog can be a good starting point.

%d bloggers like this: