Rechercher dans ce blog

mercredi 2 mai 2012

13. YoctoTemplate for GWT

We have seen in the Yocto-common post what was required for the YoctoTemplate. Let's now implement the functions for GWT. In this case, we will only support the asynchronous approach.

YoctoTemplate

 
29    
30   public class GWTYoctoTemplate implements YoctoTemplate { 
31    
32       private Logger logger = Logger.getLogger(this.getClass().getName()); 
33    
34       private String url; 
35    
36       public GWTYoctoTemplate(String url) { 
37           this.url = url; 
38       } 
39    
40       private native JavascriptYoctoMap asYoctoMap(String json) /*-{ 
41           eval('var res = ' + json); 
42           return res; 
43       }-*/; 
44    
45    
46       public YoctoMap query(String relativePath) { 
47           throw new IllegalStateException("Not implemented yet"); 
48       } 
49    
50       public void aSyncQuery(String relativePath, final YoctoCallback<YoctoMap> listener) { 
51    
52           logger.info("querying the url: " + url + relativePath); 
53           String newUrl = url + relativePath; 
54    
55           // Send request to server and catch any errors. 
56           RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, newUrl); 
57    
58           try { 
59               builder.sendRequest(null, new RequestCallback() { 
60                   public void onError(Request request, Throwable exception) { 
61                       logger.severe("Couldn't retrieve JSON"); 
62                       logger.severe(exception.toString()); 
63                       listener.onError(exception); 
64                   } 
65    
66                   public void onResponseReceived(Request request, Response response) { 
67                       if (200 == response.getStatusCode()) { 
68                           logger.info("success"); 
69                           String result = response.getText(); 
70                           JavascriptYoctoMap map = asYoctoMap(result); 
71                           listener.onSuccess(map); 
72                       } else { 
73                           listener.onError(new IllegalStateException("Couldn't retrieve JSON (" + response.getStatusCode() + ") (" + response.getStatusText() 
74                                   + ")")); 
75                       } 
76                   } 
77               }); 
78           } catch (RequestException e) { 
79               logger.severe("Couldn't retrieve JSON"); 
80               listener.onError(e); 
81           } 
82       } 
83   } 


Let me describe what happens here:
  • At line 46: The synchronous query will always trigger an exception. It does not make sense to support the synchronous call due to the architecture of javascript.
  • At line 50, we implement the asynchronous query
  • At line 56, we create the request builder for the specified URL with a method GET
  • At line 59, we ask the request builder to contact the URL. We do not expect to have the result immediately but instead in a callback
  • At line 60, we create the error callback function which will redirect the error to the caller directly
  • At line 66, we create the callback function which will parse the result: the result will be available through the function response.getText()
  • At line, we evaluate the string as a JSON object. The function is declared at line: 40
How do we convert the string into an object? That is where JSNI comes in the picture. JSNI provides a method to call directly javascript and receive the result as well. In this example, we simply say to GWT that the function is a JSNI and the code itself is evaluating the string and returns it. As long as the GWTYoctoMap class extends JavaScriptObejct, GWT will now how to cast it into the object. 

Note: In this sample, you may hit an exception if you forget to set the result of the eval function in a variable: 'var res = '

JavaScriptYoctoMap

Let me describe now how to parse the JSON object in the YoctoMap object.

 
26    
27   public class JavascriptYoctoMap extends JavaScriptObject implements YoctoMap { 
28    
29    
30       protected JavascriptYoctoMap() { 
31       } 
32    
33       public final native JavascriptYoctoMap getMap(String name) /*-{ 
34           return this[name]; 
35       }-*/; 
36    
37       public final native String getString(String name) /*-{ 
38           return this[name]; 
39       }-*/; 
40    
41       public final native int getInt(String name) /*-{ 
42           return this[name]; 
43       }-*/; 
44    
45       public final native JavascriptYoctoList getList(String name) /*-{ 
46           return this[name]; 
47       }-*/; 
48    
49       public final native int size() /*-{ 
50           if ('__gwt_ObjectId' in this) { 
51               return Object.keys(this).length - 1; 
52           } 
53           return Object.keys(this).length; 
54       }-*/; 
55    
56       public final native String getKey(int index) /*-{ 
57           return Object.keys(this)[index]; 
58       }-*/; 
59    
60    
61       public final native String getString(int index) /*-{ 
62           return this[Object.keys(this)[index]]; 
63       }-*/; 
64    
65       public final native int getInt(int index) /*-{ 
66           return this[Object.keys(this)[index]]; 
67       }-*/; 
68    
69       public final native JavascriptYoctoMap getMap(int index) /*-{ 
70           return this[Object.keys(this)[index]]; 
71       }-*/; 
72    
73       public final native JavascriptYoctoList getList(int index) /*-{ 
74           return this[Object.keys(this)[index]]; 
75       }-*/; 
76    
77    
78   } 
79   


  • As said before, JSNI knows how to cast objects so we do not have to bother about the cast however, there is no auto boxing, you have to say to JSNI which object you want to receive, so even if the javascript is always the same, you have to create multiple signature...
  • At line 33, we can see that JSNI can cast complex objects like JavaScriptYoctoMap (as long as it extends JavaScriptObject)
  • At line 49, there is a little trick. If you try to get the keys in a JavaScriptObject and you are running with the debug mode, a key is created automatically (usually the last one): "__gwt_ObjectId", in order to avoid problem in the array size, we check if the object contains the key or not.
  • If a cast fails in JSNI, the error message is usually a bit cryptic and it is better to use directly a javascript debugger to understand where the error comes from.

Aucun commentaire:

Enregistrer un commentaire