Sep 29, 2009

JSNI - Javascript Native Interface

JavaScript Native Interface (JSNI)

JSNI is GWT’s mechanism to allow you as a programmer to embed JavaScript in the Java code. We can’t overemphasize the point that JSNI is almost a last-resort approach. Many issues that look like they need JSNI can be solved at the GWT Java level if you spend time looking. It’s possible to view the relationship between GWT Java and JSNI as you do the relationship between a high-level programming language and assembly code: Yes, you can do it, but it’s sensible only if there is a clear need to do so.


It’s possible to carry this analogy further, because JSNI, just like assembly language, is less portable across systems. If you write something in JSNI that works in one browser, there is a risk that it may not work at all, or perhaps may work in a different way, in other browsers. As an example, you can count the number of children for a DOM element using the simple Java GWT DOM.countChildren() method. If you were to write that method in JavaScript, you would have to, as GWT does for you, write several versions to cope with DOM differences between Internet Explorer and the other browsers (check out the DOMImplStandard and DOMImplIE6 classes in the GWT gwt-user.jar file to see the GWT definitions for this method in the different browsers). In JSNI, you can write only one of these methods, or you would have to add the own browser detection JavaScript as well. This isn’t in the spirit of GWT, which advocates writing once and running in many different browsers. There is also the risk that writing your own JSNI code could introduce memory leaks, unless you’re an expert at those matters.


  • NOTE JSNI is applicable only to client-side aspects of your application, because JavaScript doesn’t run on the server side. It’s therefore not possible to use JSNI code in any server-side code or to pass an object over remote procedure calling (RPC) to the server and expect to be able to execute any included JSNI code server-side.

However, let’s be a little more positive about JSNI. In the cases where you do have to use it, it can be powerful. JSNI lets you interface between Java and JavaScript in a type-safe way; you can use JavaScript objects in the Java code and rely on Java’s strong typing to protect you against various programming errors. But again, the more functionality you include in a single JSNI block, the less you can rely on Java’s strong typing to minimize errors.

JSNI also provides a seamless way of moving between Java and JavaScript, allowing you to pass objects and exceptions across the boundary in both directions. Through JSNI, you manage JavaScript objects from Java, and you can call back to the Java code from the JavaScript code you write. One use is to wrap a third-party JavaScript library, where you create JavaScript objects from the library and pass them around the GWT Java code before perhaps sending them back to the JavaScript library. One word of warning about JSNI: It’s potentially a moving target during the early stages of GWT adoption. The existing model works well in most cases, as you’ll see later in this chapter, but already several requests for reviews have been published about changing functionality of certain aspects. A guiding principle of JSNI coding is to spend as little time as possible in “the dark side”; each JSNI method should be at an atomic level (perform one clear function) so that you isolate issues and keep as much control as possible in the strongly-types Java realm. Let’s move on and assume that you’re in a situation where JSNI is the approach you need. The first thing you need to understand is how to use the syntax.

Understanding JSNI

If you’re familiar with writing native methods in Java for other languages, then JSNI will seem relatively familiar. If you’ve never written native methods before, don’t worry; their syntax is a little strange but not frightening. Java Native Interface (JNI) is the Java approach that allows Java code to interface with components written in other languages, for example C or C++ or assembly. JSNI is the GWT Java equivalent for interfacing with JavaScript components, and it follows a syntax similar to that of JNI.

In the next few sections, we’ll look at the syntax used to cross the boundary both ways between Java and JavaScript and how the objects you pass over that boundary are treated. Here’s a simple JSNI method call:


public static native void method_name(ObjectTyp someData)
/*-{
someData.@org.gwtbook.client.Data::data1 == "GWT In Action"
}-*/;

We hope that doesn’t look too scary, even with the @ and :: symbols sprinkled in. To start to understand why these are there, we’ll first look at how you call Java-Script functionality from a GWT Java program.

Crossing the boundary from Java to JavaScript

To include JavaScript code in a GWT application, you must write it in a specific way so that both the syntax checkers of Java and the GWT Java compiler can recognize it and deal with it appropriately. For syntax checkers, that means ignoring the code because it isn’t Java; and for the GWT compiler, it means merging it in a structured way into the JavaScript output of compilation.

A basic JSNI method is defined as shown here:

In the template, you define the method in a normal Java way, but you must include the keyword native as one of the modifiers (1); this identifies the code to the Java compiler as JNI code and to the GWT compiler as JSNI code. To help any syntax checkers know that they should avoid parsing the JavaScript code, you wrap it as a comment by using a modified standard comment, which starts with the characters /*- (a forward slash, an asterisk, and a dash) and ends with -*/ (a dash, an asterisk, and a forward slash) (2). It’s also important not to forget the trailing semicolon at the end of the definition; otherwise your code won’t compile!

Crossing the boundary from Java to JavaScript can come in two forms: writing JavaScript code in the Java application that performs some dedicated functionality, or writing JavaScript in the Java application that calls functionality in a Java- Script library already loaded into the web browser. Both methods follow the same syntax of extending the previous template to provide parameters, a return type if necessary (otherwise the return type must be defined as void), and the native code. You can consider this crossing of the boundary diagrammatically as shown in figure-1

Figure - 1 The interaction between Java and JavaScript code when crossing the Java-to-JavaScript boundary in a JSNI call from a GWT application

Here is some typical code required to cross the boundary from Java to JavaScript:



Overall, the definition is the same as a normal Java method; you provide a list of parameters that can be passed in and a possible return object type. If you provide a return type, rather than just a void, then the JavaScript must return an object of the correct type. (Be aware that JSNI code can’t create new Java objects; it can manipulate ones passed in or create new JavaScript objects, but not new Java objects.)

  • REMEMBER It isn’t possible to create new Java objects in a JSNI code block. Return types must either be primitive types, manipulated Java objects that have been passed in as input parameters, or references to newly created Java- Script objects (a JavaScriptObject type).

It’s important to understand how the objects you provide as parameters are handled across the Java-to-JavaScript boundary, as you’ll see in the next section.

Passing Java objects across the Java-to-JavaScript boundary


We have mentioned before that one of the benefits of using Java to develop the Ajax/rich Internet applications is the strong typing provided by the Java language, which isn’t present in JavaScript. This missing typing model could cause problems as you start crossing the boundary from Java to JavaScript.
Unfortunately, there isn’t much you can do about the typing capabilities of JavaScript. Once the objects have passed into the realm of JavaScript, they’re sadly on their own. This brings us back to the point we mentioned earlier—the shorter amount of time you can spend in this potentially lawless world of JavaScript, the better. If you have to spend a long time there, it’s preferable that you do so only to interact with stable third-party JavaScript libraries. What you can do, though, is ensure that a clearly defined and repeatable mapping exists between Java typed objects and JavaScript untyped objects, which is what GWT provides.

Primitive Java numeric types, such as byte, short, char, int, long, float, or double, become simple objects in JavaScript whose value is that of the original object. For example, if you have a Java char, char keyPress = 'a', then it will become the JavaScript variable var k = 'a'. Similarly, the Java int, int val = 10, becomes the JavaScript variable var v = 10.

A Java String is translated across into JavaScript as a simple variable to which the original text is assigned. Therefore the Java object String name = "GWT In Action" becomes the JavaScript variable var s = "GWT In Action". Finally the simple Java boolean, becomes another simple JavaScript variable; the Java boolean b = true becomes the JavaScript variable var b = true.

Moving on to more complicated Java objects that can be passed across the boundary, you have the Java array, a Java object, and a new object to GWT called the JavaScriptObject. We’ll discuss the last object more in the next section; for now, you should think of it as just a reference to a JavaScript object created somewhere else that can be passed around the Java code and on to other JavaScript methods—but you can’t look into its contents from the GWT Java code. This may sound strange, but we’ll explain in more detail later in the section on this object.

Passing an array across the Java-to-JavaScript boundary is treated in a similar opaque way; you know you have an array object, but you can’t look into its contents. This means that if the JavaScript code needs to use values in the array, then you should move them out of the array before you cross the boundary: either into separate parameters or into another type of user-defined Java object. You can manage Java objects in JavaScript through the JSNI interface, as we’ll discuss next.

You can also pass your own defined Java objects across the Java-to-JavaScript boundary; GWT provides a special syntax allowing you to access the fields and methods of that Java object. This will take a little bit of explaining, so please, stick with us.

Let’s say you have an object of type Data, which is defined by the class shown below

Example class that will be accessed through the Java-to-JavaScript boundary


package org.gwtbook.client;
public class Data{
String data1 = "GWT In Action";
int version = 1;
}

You use an instance of this type in a new class defined called DataManip

Example class demonstrating accessing Java objects through the Java-to-JavaScript boundary


In the DataManip class, you have a couple of additional class variables: one called correct (1)and then other a static variable called checked (2)(it doesn’t matter what they stand for in this example). You also define a JSNI method (3) called doSomething(), which takes as a parameter an instance of the Data class but returns nothing (we’ll cover going across the JavaScript-to-Java boundary in a short while). When doSomething() is called, you want it to look at the DataManip class’ correct value (4); if that is false, then you want to check the static field checked (5). If that too is false, then you confirm whether the Data instance passed in as a parameter has its data1 field set to “GWT In Action” and the version value set to 1 (6); if they are, then you do something else in the code. To perform this functionality, you need to access an instance field, a static
field, and a field in a Java object passed in as a parameter, in that order. In each case, you need to use the JSNI-specific syntax for accessing Java objects, the template for which is shown in figure - 2


Figure - 2 Explanation of the JSNI method to access a field in a Java object. The first part comprises the name of the instance (an object name, the keyword this, or blank for a static field). Next are the fully
qualified class name and the field name.

When you access the instance field (4), the name of the instance is this, the class name of this instance is org.gwtbook.client.DataManip, and the field you’re after is called correct. Accessing this field is, therefore, performed by writing the following in the JSNI code:


this.@org.gwtbook.client.DataManip::correct

Accessing the static field (5) requires you to access a field where there is no defined objectName (because the field is static across all instances). In JSNI, you write


@org.gwtbook.client.DataManip::checked

(Notice that the period is missing and only the @ symbol is still required if there is no object name to reference.)

Finally, when you want to reference the data1 field in the parameter that you’ve passed in, then the parameter name, someData, is the objectName, and you write


someData.@org.gwtbook.client.Data::data1

You can also access the methods from the Java object passed across the boundary in a similar way. We’ll discuss how that works once you’ve seen how you return objects back across the boundary to the Java code.

Sending objects back across the JavaScript to Java boundary

When you’ve completed the functionality you need in JSNI, it’s common to pass an object back in return. As with all Java methods, you need to define the return type of the JSNI method, which will either be a Java primitive, a Java object, a GWT JavaScriptObject, or void. Diagrammatically, you’re performing the action shown in figure -3 ; once complete, program control is back in the hands of the GWT Java code.

Just as there was a defined mapping between Java and JavaScript objects for the parameters, such a mapping exists when going from JavaScript to Java for the return values.

To get a Java primitive numeric coming out of the method call, the value returned from the JavaScript must be a numeric value. You need to be careful with returning primitive numerics because GWT doesn’t determine whether the type is correct. If you define the Java method to return a Java int, and the Java-Script returns the value 1.5, then the result passed out of that surrounding Java method will be unpredictable.


Returning Strings and booleans is much simpler because they translate directly to their Java equivalents. Java objects can also be returned from the Java-Script; however, you can’t just create Java objects in the JavaScript. If you want to return a Java object, then it must have been passed in as one of the parameters.


Figure - 3
Examining the interaction between Java and JavaScript code when returning values across the JavaScript-to-Java boundary in a JSNI call from a GWT application

Take care if you’re returning null objects, because the JavaScript undefined value isn’t treated by JSNI as null; if it’s used, unpredictable results can occur (you must always use the null value). You may even want to ensure that any JavaScript variable you pass back is checked to be sure it isn’t the undefined value before it’s returned.


We mentioned briefly when passing in parameters that GWT provides a special object called the JavaScriptObject. This type of object can be returned by the JSNI method if a new JavaScript object is created as part of the call. You’ll see this in use often later in this chapter when you use JSNI methods to create new Java-Script objects from a third-party library. You need a reference to these third-party objects in the Java code because you’ll later be calling methods on them, and they’re therefore passed back as subclasses to the JavaScriptObject. This Java-ScriptObject is opaque to the Java code—you can’t use Java code to call the methods in it or even to see its fields; you need to pass them back into new JSNI methods to do that. But you’ll see this in action a little later. First we need to finish our journey through the JSNI syntax by looking at how you can call Java methods
from JavaScript.

Crossing the boundary from JavaScript to Java

As well as being able to access fields on Java objects from JavaScript, you can execute methods defined in those objects in a similar manner, as shown in figure - 4

Figure 4 Examining the interaction between JavaScript and a Java object when crossing the JavaScript to Java boundary in JSNI

To refer to a method, you use a syntax similar to that you used to refer to fields. There are, however, some slight differences. This time, the generic template for calls to methods is shown in figure - 5

The first part of this should be familiar from when you were accessing fields, with the same discussion about there being no objectName if you’re accessing a static method, using this if you accessing a method in the current instance, and using the parameter name if you’re accessing a passed-in object to a method. The difference occurs in the second half of the template, where you see the values param-signature and arguments. The arguments part is a list of the various arguments whose types match the parameter signature. JSNI uses the internal Java


Figure 5 - Explanation of the JSNI method to access a method in a Java object. First is the
instance name, followed by the fully qualified class name. The method name is followed by the
parameter signature and then the arguments.

method signature to define the parameter signature, but it doesn’t require a definition of the return type to be included. Parameter type signatures are defined in below table.

Java type signature for various Java types

Let’s look at a simple example

Sample attempts to call methods in a Java class from JavaScript



In (1), you call the m1() method in this class, which takes a Java String as its parameter; so, you must define the parameter signature as Ljava/lang/String;. Then, you call the m2() method (2), which expects an integer, so the method signature is I. (In your code, these definitions should all be on one line—it’s sometimes difficult to get all of them on one line in the book!) The final type of objects that can come out of the JSNI call are exceptions.

Handling exceptions


It’s strongly recommended that JavaScript exceptions be handled in the Java-Script segments of the JSNI method and that Java methods be handled in Java code. The reason is that when a JavaScript exception creeps over the JavaScript boundary into the Java, it becomes, and can only become, an object of type
JavaScriptException.

You don’t necessarily lose information about the JavaScript exception, because you can use the getName() and getDescription() methods to return String values about the original JavaScript exception. But relying on these methods leads to messy code requiring the use of String comparison to handle the exceptions, whereas they can be handled more gracefully in the JavaScript code. The only exception to this rule is where an exception is raised in Java code called by a JSNI method, which is then handed back to more Java code. In this case, the original Java exception typing is preserved through the boundaries.
With all these basics in place, let’s look at the occasions when we said you may need to use JSNI, starting with how you perform different types of communication between components (browser, GWT applications, and legacy code).

Summary

JSNI is an extremely powerful way of interfacing with existing JavaScript libraries and filling in the gaps where GWT may not yet have the functionality you need. It allows you to apply some of the good control and typing aspects of Java to the Java-Script interfaces; however, you should keep your use of JavaScript to a minimum. View using JSNI as you would writing assembly-language code segments in a highlevel language project—it’s for specialized tasks, not for everyday use. The main reason for restricting the use of JavaScript is cross-browser issues, just as assemblylanguage coding restricts your ability to move projects to other machines.

If you’re going to use JSNI, remember that it’s valid only in your client-side components, and you’ll need to use the $wnd and $doc variables instead of the standard JavaScript window and document variables because of the way GWT loads your application. Also remember that when you’re executing JavaScript code, you don’t have the protection of GWT when it comes to browser differences (for example, the JavaScript getElementById() method isn’t as robust as the GWT’s cross-browser DOM.getElementByID() method). Finally, GWT and JSNI don’t allow you to get around any normal restrictions of JavaScript coding; for example, you still can’t subvert the security mechanisms that browsers employ.

We’ve reached the end of our journey through the more client-side related aspects of GWT, and it’s time to consider the flexibility that GWT provides when building a real-sized application—using GWT’s modularization aspects.

0 comments:

Text Widget

Copyright © Vinay's Blog | Powered by Blogger

Design by | Blogger Theme by