Timing issue when using JSNI in gwt.

216 views
Skip to first unread message

Ali

unread,
May 19, 2012, 12:34:17 AM5/19/12
to Google Web Toolkit
Hi there,
I have a gwt project who uses JSNI methods to load values from
javascript into Java.
the myProject.html contains a script tag referring to javascript file
containing javascript files.
I call the JSNI methods directly from onModuleLoaded() method to
return the javascript values.
Running the application in development mode, works OK, but running it
after compile 'and deploying war file to the server side doesn't run
and indicates
some timing issues which I guess is about the the javascript/html not
yet loaded when the application uses JSNI methods.
The following are the code snippets:
- "myProject.html" in the 'war' folder contains the following script
tag to include the javascript file:

<script type="text/javascript" language="javascript" src="scripts/
browserSupport.js"></script>

- the java script file (in my case 'browserSupport.js') simply
contains the following variable:

brVersionMap={"IE":"8","Firefox":"15","Chrome":"17","Safari":"5"};

- From inside onModuleLoad() of "MyProject.java" I call the JSNI
methods defined in "myProject.java"

public class MyProject implements EntryPoint {

public void onModuleLoad() {
...

JavaScriptObject jsBrVerObj = (JavaScriptObject) getBrVersionMap();

//process jsBrVerobj ...

}


public static native Object getBrVersionMap() /*-{
return $wnd.brVersionMap ;
}-*/;
}

I need to invoke the JSNI method "getBrVersionMap()" after I know the
html/javascript has been fully loaded and ready.
Any help is appreciated.

JoseM

unread,
May 19, 2012, 4:02:22 PM5/19/12
to google-we...@googlegroups.com
You could use ScriptInjector to insert your external javascript file onto the Page. It has a callback that will be invoked once it has been loaded.

You can also test to see if your $wnd.variable is undefined before trying to use it and if it is then use a timer or schedule deferred or repeating to wait until it is available.

Joseph Lust

unread,
May 19, 2012, 5:54:56 PM5/19/12
to google-we...@googlegroups.com
Ali,

Script tags are blocking at load. They have to be this way or you could never have JS lib dependencies since a dependency might not yet have been loaded when another script calls it.

So, if your <script> tags for browserSupport.js come before the tags to load the GWT bootstrapper in the HTML, then it should be in the page scope and available for GWT. 

Also, if the contents of the JS file are so simple, could they be moved directly to GWT? It looks like you're using this to be able to change your minimum supported browser versions through the static JS file, but given that a change in these requirements should require a new version of your application to be deployed, why not store these in a properties file in GWT so that they are compiled into your application?

Of course I don't know the details of your implementation, but perhaps there is a way around this issue entirely.



Sincerely,
Joseph

Ali

unread,
May 19, 2012, 9:13:23 PM5/19/12
to Google Web Toolkit
Hi Joseph,
Thank you for your reply.
Having the browser version numbers in a javascript file available on
the server is a requirement coming from product owner.
They want to have it available even between the releases for possible
changes by administrator.

I tried putting the <script> tag for browserSupport.js before GWT
selection script tag (in my case: <script type="text/javascript"
language="javascript" src="thinclient/thinclient.nocache.js"></script>
and still didn't work. The only browser that seems to work is IE (and
this was the case even before this change). All other browsers
(Firefox, Chrome, Safari) still wouldn't load the page.
Is there any way to fix this using the BrowserSupport.js and not a
property file?
Thanks again.



On May 19, 1:54 pm, Joseph Lust <lifeofl...@gmail.com> wrote:
> Ali,
>
> Script tags are blocking at load<https://developers.google.com/web-toolkit/doc/latest/DevGuideOrganizi...>.

Joseph Lust

unread,
May 20, 2012, 3:32:19 AM5/20/12
to google-we...@googlegroups.com
Ok, so I was ruminating over this one. I just modified the default web project to the code you supplied and it worked fine. Not sure what is different between my code and yours.

The JSO class
package com.testbrowser.client;

import com.google.gwt.core.client.JavaScriptObject;

public class BrowserVersionList extends JavaScriptObject {
       
protected BrowserVersionList() {
       
}

       
public static BrowserVersionList newInstance() {
               
return createJso();
       
}

       
private static native BrowserVersionList createJso() /*-{
                return $wnd.brVersionMap;
        }-*/
;

       
public final native String getChromeVersion() /*-{
                return this.Chrome;
        }-*/
;
}

The version js file:
brVersionMap={"IE":"8","Firefox":"15","Chrome":"17","Safari":"5"};

The main onModuleLoad body to show the version found:
public class BrowserSupportVersion implements EntryPoint {

        /**
         * This is the entry point method.
         */

       
public void onModuleLoad() {

               
BrowserVersionList browserVersions = BrowserVersionList.newInstance();
                GWT
.log("Chrome version supported: "+browserVersions.getChromeVersion());


               
// Create the popup dialog box
               
final DialogBox dialogBox = new DialogBox();
                dialogBox
.setText("Detecting supported browser versions");
                dialogBox
.setAnimationEnabled(true);

               
// We can set the id of a widget by accessing its Element
               
final Label textToServerLabel = new Label();
               
VerticalPanel dialogVPanel = new VerticalPanel();
                dialogVPanel
.addStyleName("dialogVPanel");
                dialogVPanel
.add(new HTML("<b>Supported Chrome version: <i>"+browserVersions.getChromeVersion()+"</i></b>"));
                dialogVPanel
.add(textToServerLabel);
                dialogVPanel
.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
                dialogBox
.setWidget(dialogVPanel);

               
RootPanel.get("notificationContainer").add(dialogBox);
       
}
}
I put this up and a version of my AppSpot account and it works from IE8, FF12, and Chrome 18.
http://4.runpartner.appspot.com/

Let me know if it works for you. Not sure what's causing your error. ( I cleaned the code up a little, but it ran as you had it too.)

Sincerely,
Joseph

Ali

unread,
May 20, 2012, 4:44:59 PM5/20/12
to Google Web Toolkit
Hi Joseph,
Thanks again, your reply helped a lot and I did some minor changes to
my code.
But I found the real culprit. I can get the GWT JavaScriptObject
inside my program. but I am using a smartGWT JSOHelper class to
convert the returned javascript object to a map.
JSOHelper.convertToMap(brVersions, and this is exactly where the
application hangs.
Does GWT offers something like this? I have a loop to go over the
map, instead of calling different javascript methods to get the
version numbers.

Thanks again.
Ali
> *http://4.runpartner.appspot.com/*

Joseph Lust

unread,
May 20, 2012, 6:05:56 PM5/20/12
to google-we...@googlegroups.com
Ali,

I've been meaning to learn a lot more about JNSI, so this was a good excuse. Here is how to read it into a map. If anyone else knows of a better way (all I can find on Google are people asking how), please suggest it.

Revised browser object: Since we don't need to extend JSO, we can just use JSNI to load the values and store them in a standard map. You could add some null checking to the JSNI method for the case where the JS file was never loaded.
package com.testbrowser.client;

import java.util.HashMap;
import java.util.Map;

public class BrowserVersionList {
       
       
Map<String,Integer> versionMap = new HashMap<String,Integer>();
       
       
public BrowserVersionList() {
                loadValues
(this);
       
}
       
       
/**
         * Fetch the minimum supported browser version
         * @param browserName
         * @return NULL if no match found
         */

       
public int getVersion(String browserName) {
               
return versionMap.get(browserName);
       
}
       
       
public void putVersion(String key, int value) {
                versionMap
.put(key, Integer.valueOf(value));
       
}

       
/**
         * Fetch values from JS object in browser.
         * @param instance
         */

       
public final native void loadValues(BrowserVersionList instance) /*-{
                for( var key in $wnd.brVersionMap) {
                        var value = $wnd.parseInt($wnd.brVersionMap[key],10);
                        instance.@com.testbrowser.client.BrowserVersionList::putVersion(Ljava/lang/String;I)(key,value);
                }
        }-*/
;
       
}

And here is the updated code in the onModuleLoad() that is accessing the above object.
package com.testbrowser.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */

public class BrowserSupportVersion implements EntryPoint {

       
/**
         * This is the entry point method.
         */

       
public void onModuleLoad() {


               
BrowserVersionList browserVersions = new BrowserVersionList();
               
int chromeVersion = browserVersions.getVersion("Chrome");
                GWT
.log("Chrome version supported: "+chromeVersion);



               
// Create the popup dialog box
                ... same as before
       
}
}

Given that you're in SmartGWT, hopefully you won't need to worry about JSO vs JSOHelper now.


Sincerely,
Joseph

Jens

unread,
May 20, 2012, 6:50:52 PM5/20/12
to google-we...@googlegroups.com
Seems pretty similar to what the Dictionary class of GWT does (You have to convert the string return type into an integer. Dictionary only knows strings).



-- J.

Brandon Donnelson

unread,
May 20, 2012, 7:31:07 PM5/20/12
to google-we...@googlegroups.com
I've done this type of thing in a few different ways depending on the project. If I don't have access to the Javascript object I have polled its load. If you have access to the BrowserSupport Object, I'd add a callback type of method at the end of it to tell your GWT module when the JS object/script has loaded loaded by calling a JSNI method inside your GWT module. You could then finish rendering your GWT module after the script is loaded. That said this might not be something you like. The next method I've done is something like the Deferred Binding with RichTextEditor, depending on the browser you can script inject the JS version you need and load depending on browser. I think there are quite a few ways you could asyncly load JS and GWT module and have them talk to each other telling them what to do :) 


Brandon Donnelson

Joseph Lust

unread,
May 20, 2012, 8:21:34 PM5/20/12
to google-we...@googlegroups.com
Great point Jens, the dictionary method is clearly the simplest for this use case:

Dictionary supportedVersions = Dictionary.getDictionary("brVersionMap");
int chromeVersion = Integer.valueOf(supportedVersions.get("Chrome"));


Sincerely,
Joseph

Ali

unread,
May 22, 2012, 12:48:33 AM5/22/12
to Google Web Toolkit
Thank you Joseph, Jens and Brandon.
The GWT Dictionary is exactly what I need for this use case.
Joseph, thanks again for all your help.

Regards,
Ali.

Ali

unread,
May 22, 2012, 3:59:34 PM5/22/12
to Google Web Toolkit
Hi Joseph,
Have you tried using the Dictionary for this. I am having the same
issues again using Dictionary.
So using Dictionary, we don't have to call JSNI to read the map in and
we directly use the javascript variable name to get a Dictionary
object.

I tried this and again running it in dev mode is OK, but in Hosted
mode not OK.

Joseph Lust

unread,
May 22, 2012, 6:49:14 PM5/22/12
to google-we...@googlegroups.com
It worked fine more me in hosted mode, using the code snippet for Dictionary included earlier. However, do note that __gwt_ObjectId is added to the set when using Hosted Mode. You should be able to ignore it.

Sincerely,
Joseph

Ali

unread,
May 23, 2012, 12:26:49 AM5/23/12
to Google Web Toolkit
Hi Joseph,
Both your solution works. My bug was somewhere else.
Even JSOHelper.convertToMap() works.
I had a bug using GWT regular expressions which didn't show up as a
bug in dev mode. I had to run my program several times in hosted mode
to find it.

Thanks,
Ali


On May 22, 2:49 pm, Joseph Lust <lifeofl...@gmail.com> wrote:
> It worked fine more me in hosted mode, using the
> code snippet for Dictionary included earlier. However, do note that *__gwt_ObjectId
> *is added to the set when using Hosted Mode. You should be able to ignore
> it.
>
> Sincerely,
> Joseph
Reply all
Reply to author
Forward
0 new messages