GWT JSNI method call failing, but there are no errors - gwt

I'm trying to implement Mozilla's Persona in a GWT App. Here's part of the code from a dummy app I set up to test it:
public class OpenId implements EntryPoint {
private native void callWatch(String email)
/*-{
$wnd.navigator.id.watch({
loggedInUser: email,
onlogin: function(assertion){
$wnd.alert("Calling method");
this.#com.gallup.openid.client.OpenId::processLogin(Ljava/lang/String;)(assertion);
$wnd.alert("Called Java Method");
},
onlogout: function(){alert("Logged Out!");}
});
}-*/;
private void processLogin(String assertion){
Window.alert("Logged in!");
personaStatus.setText("Log In Complete.");
}
}
When I call the callWatch method, only the "Calling method" alert box shows up. Neither of the other ones are ever called. So for some reason the code appears to be stopping at the JSNI call right below the first alert. But there are no errors in Dev Mode.
I don't understand why the processLogin method doesn't get called.
I thought I followed Google's Documentation correctly.
I did try writing
this.#com.gallup.openid.client.OpenId::processLogin(Ljava/lang/String;)(assertion);
as OpenID.#... and instance.#... due to this post.
I'm not sure what else to try.

The variable this points to the function that immediately surrounds it, which is in this case your onlogin JavaScript function. You need to use a temporary that variable (a typical JavaScript idiom, by the way)
private native void callWatch(String email)
/*-{
var that = this;
...
onlogin: function(assertion){
that.#com...
And then, ideally use $entry(...), so you will see error messages, if you have registered an UncaughtExceptionHandler.
See also: https://stackoverflow.com/a/5235580/291741

Related

How to make wait some time in GWT after the Async call

In my code, I am making async call to do validation. Depeding upon return value of the validation, I need to execute some lines.
But I am not able to put that lines in the callback method of Async = public void success(Boolean valid).
Since one of the line is super.onDrop(context) which is method of another class that can't be called inside Async callback method.
Please see the below line. I need super.onDrop(context) will be executed after async call is completed.
stepTypeFactory.onDropValidation(stepTypeFactory,new AsyncCallbackModal(null) {
public void success(Boolean valid) {
if(valid==Boolean.TRUE){
//super.onDrop(context);
}
};
});
//condition is here
super.onDrop(context);
Is there any way, i will tell gwt wait 1 or 2 seconds before super.onDrop(context) is executed. Right now what happening is,
super.onDrop(context) is executed before the call back method is completed.
You can do:
stepTypeFactory.onDropValidation(stepTypeFactory,new AsyncCallbackModal(null) {
public void success(Boolean valid) {
if(valid==Boolean.TRUE){
drop();
}
};
});
private void drop() {
super.onDrop(context);
}
An alternative solution would be, like mentioned from Thomas Broyer in the comments:
stepTypeFactory.onDropValidation(stepTypeFactory,new AsyncCallbackModal(null) {
public void success(Boolean valid) {
if(valid==Boolean.TRUE){
ContainingClass.super.onDrop(context);
}
};
});
Eclipse does not suggests this solution when using the code completion, but it works.
Also i would possibly reconsider your design, because it can get very tricky (by experience) when you have many Callbacks which are connecting/coupling classes. But this is just a quick thought, i neither know the size of your project nor the design.

How to implement a javascript API using JSNI?

I am trying to implement an API (SCORM API) using GWT.
The client code expects an API object with methods like Initialize(), getLastError() and so on...
I tried to implement this api as an Java Object, but i see that the compiled names are changed and cannot be used directly by client code.
I see that gwt-exporter can do the trick (http://code.google.com/p/gwt-exporter/) but i would like to know how to do it using pure gwt and jsni.
As the API is expected as a object, named API_1484_11 attached to the window object, not an function, , i don't see how to use the $entry() idiom.
Here is my current, failing, code:
public final class SCORMApi {
protected SCORMApi() {}
public void Initialize(){
GWT.log("** INITIALIZE CALLED **");
}
public static void create(){
bind(new SCORMApi());
}
public static native void bind(SCORMApi api) /*-{
$wnd.API_1484_11 = api;
}-*/;
}
So, in this context, my question is:
How can i get javascript calls (e.g. window.API_1484_11.Initialize() ) to reach my java gwt code?
You're on the right lines with your bind method. But you haven't understood how to call Java methods from within JSNI. This is how you do it in the case of your Initialize method:
public static native void bind(SCORMApi api) /*-{
$wnd.API_1484_11 = {
initialize: function() {
$entry( api.#com.yourpackage.name.SCORMApi::Initialize()() );
}
};
}-*/;
The blogs Getting To Really Know GWT parts 1 and 2 are required reading on this subject.

Linking to GWT instance method from JSNI does not automatically bind "this"

I am going to file this as a bug report, but I wanted to check if someone here can see something wrong with what I am doing.
When you expose an instance method from a GWT class through JSNI, this works as expected in JavaScript. Since we are cross compiling Java, I would instead expect this to be bound to the instance automatically. For example:
package com.test;
class Foo {
public void instanceFunction() {
this.otherFunction() // will cause an error when called from JSNI!
}
public void otherFunction() {
// does some stuff
}
public native JavaScriptObject getInstanceFunction() /*-{
return this.#com.test.Foo::instanceFunction();
}-*/;
}
Currently the workaround is to bind the function yourself (not very portable):
public native JavaScriptObject getInstanceFunction() /*-{
return this.#com.test.Foo::instanceFunction().bind(this);
}-*/;
This can also be seen as preference, some may prefer that the functions remain unbound. I would say the current functionality is unintuitave and unnecessary. I cannot imagine a use case for having an unbound this directly in Java code. Also, some browsers do not implement bind(1), so my workaround is not robust.
If you want a portable bind, it's as easy as:
var that = this;
return $entry(function() {
return that.#com.test.Foo::instanceFunction()();
});

GWT RPC possible in window closing handler?

I'm listening for a window close event:
closeHandlerReg = Window.addCloseHandler(new CloseHandler<Window>() {
#Override
public void onClose(CloseEvent<Window> event) {
// ...
}
});
The documentation says that no UI can be shown in that callback. What about GWT RPC calls? I'm trying to make one, but it isn't showing up on the server (either in breakpoints or log statements).
Problem is that GWT RPC is asynchronous and the calls to RPC services return immediately. In this case the window is closed before the browser has a chance to send the underlying XMLHTTPRequest.
If it's absolutely necessary you should be able to hand-craft calling some sort of servlet (not GWT RPC) with a "synchronous" XMLHTTPRequest. Have a look here for an example: http://weblogs.asp.net/bleroy/archive/2005/12/15/433278.aspx
But really you shouldn't be doing anything of this sort in the window.onunload or window.onbeforeunload (these are the underlying DOM events for CloseEvent and ClosingEvent for the Window. Perhaps there is a better way to do what you are trying to do.
The use case you have should be possible. When you make the RPC call in the closeHandler it should arrive at the server, because while it returns directly, it has started sending the data, and set a callback to wait for the result. However the callback will fail because the connection is lost because the window is closed. But that is no problem as you only want to notify the server. So the question might be what are you sending? and does it work at all, when you send it at some other point in the code, not in the closeHandler?
Old question, but still - maybe someone faces the same issue.
RPCs won't work in the closing handler as already discussed. This workaround worked for me:
In the onClose method, do something like:
Window.Location.replace(GWT.getModuleBaseURL() + "rpcCall?param1=" + param1 + "&param2=" + param2);
whereas "rpcCall" is the name of the rpc url you have set in your web.xml file. Of course, a random number of parameters may be passed within the URL.
Then, in your server-side implementation of your rpc interface, you can override the doGet method:
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
String param1 = URLDecoder.decode(request.getParameter("param1"), "UTF-8");
String param2 = URLDecoder.decode(request.getParameter("param2"), "UTF-8");
// do something
}
And another solution: Don't do an rpc call on window closing, but a regular http call that can then be handled by a custom servlet on the server side.
In your GWT module, do something like this:
Window.addWindowClosingHandler(new ClosingHandler() {
#Override
public void onWindowClosing(ClosingEvent event) {
sendWindowClosed(GWT.getModuleBaseURL() + "teardownservice");
}
});
private native void sendWindowClosed(String url)
/*-{
var Http = new XMLHttpRequest();
Http.open("GET", url);
Http.send();
}-*/;
}
In the server-side servlet, you can then handle this call:
public class TearDownServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
.... whatever you want ....
}
}

Clientside error (Cannot call method 'nullMethod' of null arguments: nullMethod)

My App engine/ GWT project is spitting out a nasty little pile of stack trace whenever it attempts to return from my login method. I am using GAE version 1.5.0 and GWT version 2.3.0 .
It's a facebook app, so what I've got is this:
The player navigates to the app page.
They click a button, and are redirected to the OAuth authentication page
They are then redirected back to the app, with the authentication token in the query string
I break the query string apart to get the UID, and then use that as the primary key for my Player entity (RPC to app engine backend)
I retrieve the Player entity instance from the datastore, and turn it into a serializable type to return to the client
Epic fail.
When I spit out the exception in a JSAlert, I get a big nasty pile of stack trace (I already was thoughtful enough to compile using "pretty" instead of "obfuscated").
My login function looks like this:
#Override
public ClientPlayer login(String uid) {
PersistenceManager pm=PMF.get().getPersistenceManager();
log.warning(Player.class.getName());
log.warning(uid);
Key k=KeyFactory.createKey(Player.class.getSimpleName(), uid);
Player p;
List<List<Integer>> stats;
try{
p=pm.getObjectById(Player.class, k);
} catch (JDOObjectNotFoundException e){
p=new Player(uid);
p.setKey(k);
pm.makePersistent(p);
} finally {
pm.close();
}
stats=p.getStats();
return new ClientPlayer(p.getUID(),p.getPerm(), p.getDecks(),stats.get(0), stats.get(1), stats.get(2));
}
Unfortunately, due to NDA, I can't link to the app, but here's the output:
Failure to log in because of:
com.google.gwt.core.client.JavaScriptException: (TypeError): Cannot call method 'nullMethod' of null
arguments: nullMethod,
type: non_object_property_call
stack: TypeError: Cannot call method 'nullMethod' of null
at Object.ClientPlayer_1 (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:993:89)
at Object.ClientPlayer_0 (http://*com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:984:18)
at Array.instantiate_1 [as 0] (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:1031:10)
at $instantiate_0 (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:10660:34)
at $instantiate (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:1948:10)
at $readObject (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:10148:95)
at Object.read_8 [as read] (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:10608:10)
at $onResponseReceived (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:10352:247)
at $fireOnResponseReceived (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:5002:5)
at Object.onReadyStateChange (http:/*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:5222:5)
The issue was in the use of the IsSerializable interface, or rather my poor understanding of it.
When you create an IsSerialiazable object, it requires a no-argument constructor. I was passing null values from that constructor to the main constructor, so when methods were called on them, null pointer exceptions occurred. This was dumb of me, but hey, it was a learning experience.
In my particular case, it went a little like this...
public class ClientObject implements IsSerializable {
private Object field1;
private Object field2;
private String field3;
public ClientObject(){
this(null, null);
}
public ClientObject(Object arg1, Object arg2){
field1=arg1;
field2=arg2;
field3=arg1.toString()+arg2.toString();
//Error on above line, though not obviously mentioned in the message
}
}
What should have been done was...
public ClientObject(){
this(new Object(), new Object());
}
Hope this helps someone.