JSONP Server Side - Dan Morrill Article

42 views
Skip to first unread message

eggsy84

unread,
Oct 6, 2008, 11:02:46 AM10/6/08
to Google Web Toolkit
Hi all,

Taking the informative article by Dan Morrill at GWT

Link: http://code.google.com/support/bin/answer.py?answer=65632&topic=11368

It explains how to code for Server side mashups so that you can
perform cross site JSONP calls.

In the article he uses the handle method (Shown below) to handle the
return from the server side:

public void handle(JavaScriptObject jso) {
JSONObject json = new JSONObject(jso);
JSONArray ary =
json.get("feed").isObject().get("entry").isArray();
for (int i = 0; i < ary.size(); ++i) {
RootPanel.get().add(new
Label(ary.get(i).isObject().get("title").isObject().get("$t").toString()));
}
}


I have tried writing my own basic Java Servlet GET/POST we all know
the score and I can successfully call into my servlet but I never get
back to my client side, in this case after the servlet has done its
stuff, I never go back to the handle method to perform some whizz bang
GWT stuff - is there something specific you are required to do on the
server side? Such as extends RemoteServiceServlet as you would do in a
normal GWT AsyncCallback call? Anyone got any ideas?

Thanks all


eggsy

Adam T

unread,
Oct 6, 2008, 4:28:11 PM10/6/08
to Google Web Toolkit
Eggsy,

To get it to work you need to get the plumbing right, and it's not
quite the same way as calling from code - btw, the example on that
page is aimed at client side not server side. The server is any
language you want as long as it returns a well-formed JavaScript
segment of the form:

mycallback({....some json content....})

So your servlet would work as long as it returns something like the
above.

In this approach you don't call the servlet in the normal way from the
program code, rather it gets called as a consequence of adding a
<script> tag to the DOM - this is what the addScript() method in the
example code does. Once the script is added to the DOM the browser
accesses the defined url of your service and expects a response. As
the response is a JavaScript function, it will get evaluated in the
browser.

If you also define a function in the DOM with the same name you expect
back in the server response, e.g. mycallback, and that function calls
the GWT handle() function then the loop is closed. The example code
adds such a function using the setUp() method.

The example code reserves a new function name for each "call" made to
the server, adds that new function to the DOM and then the <script>
tag.

Where things usually go wrong are if the server returns a function
name not set up, or the response is not a valid javascript expression.

Hope that helps in some small way!

//Adam


this then gets evaluated in the browser

eggsy84

unread,
Oct 7, 2008, 4:14:27 AM10/7/08
to Google Web Toolkit
Hi Adam,

Thank you for the reply it definately helps! when you say it needs to
be well-formed javascript I have implemented a method that performs
the following:

/* (non-Javadoc)
* @see
javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse
resp) throws ServletException, IOException
{
String output ="[{color: \"red\",value: \"#f00\"}]";
resp.setContentType("text/javascript");
resp.addHeader("Pragma", "no-cache");
resp.setStatus(200);
PrintWriter out = resp.getWriter();
out.println(output);
}


Would that be sufficient? Well I ask the question but I assume not as
with my implementation I successfully go back to my client handle
method now but the JavascriptObject passed in is always null.

My client side handle method is very basic as a test (shown below) and
I can confirm that when the claa is complete is definately hits this
method so the glue between is wrong somehow?


public void handleJsonResponse(JavaScriptObject jso)
{
if (jso == null)
{
Window.alert("Unable to parse JSON");
return;
}
else
{
Window.alert("Well done Woohoo!!");
}
}

eggsy

Adam T

unread,
Oct 7, 2008, 7:24:39 AM10/7/08
to Google Web Toolkit
I think what you are missing is the function name in your output from
the servlet. From what I see, you just return:

[{color: \"red\",value: \"#f00\"}]

when I would expect a response something like:

mycallback([{color: \"red\",value: \"#f00\"}])

where the "mycallback" is the name of the function you add to the DOM
which calls your handleJSONResponse method - without returning a
function from your servlet, I'm not sure how your handle method is
being called.

Also, are you defining your callback code to pick up the parameter,
e.g.

public native static void setup(YourHandlerClass h, String callback) /
*-{
window[callback] = function(someData) {
h.@com.whateveryoursis.YourHandlerClass::handleJSONResponse(Lcom/
google/gwt/core/client/JavaScriptObject;)(someData);
}
}-*/;

without the (Lcom/google/gwt/core/client/JavaScriptObject;)(someData)
part it won't pick up the returned data

//Adam

eggsy84

unread,
Oct 7, 2008, 7:46:03 AM10/7/08
to Google Web Toolkit
I expect thats what I'm missing then!

My calling method is as follows:

public native static void getJson(int requestId, String url, CallModel
handler)
/*-{
var callback = "callback" + requestId;

var script = document.createElement("script");
script.setAttribute("src", url+callback);
script.setAttribute("type", "text/javascript");

window[callback] = function(jsonObj)
{
handler.@com.mypackage.MyClass::handleJsonResponse(Lcom/google/
gwt/core/client/JavaScriptObject;)(jsonObj);
window[callback + "done"] = true;
}

// JSON download has 1-second timeout
setTimeout(function()
{
if (!window[callback + "done"])
{
handler.@com.mypackage.MyClass::handleJsonResponse(Lcom/
google/gwt/core/client/JavaScriptObject;)(null);
}

// cleanup
document.body.removeChild(script);
delete window[callback];
delete window[callback + "done"];
}
, 1000);

document.body.appendChild(script);
}-*/;

I have adapted it from a useful guide here:

http://giantflyingsaucer.com/blog/?p=126

Eggsy

On Oct 7, 12:24 pm, Adam T <adam.t...@gmail.com> wrote:
> I think what you are missing is the function name in your output from
> the servlet.  From what I see, you just return:
>
> [{color: \"red\",value: \"#f00\"}]
>
> when I would expect a response something like:
>
> mycallback([{color: \"red\",value: \"#f00\"}])
>
> where the "mycallback" is the name of the function you add to the DOM
> which calls your handleJSONResponse method - without returning a
> function from your servlet, I'm not sure how your handle method is
> being called.
>
> Also, are you defining your callback code to pick up the parameter,
> e.g.
>
> public native static void setup(YourHandlerClass h, String callback) /
> *-{
>     window[callback] = function(someData) {
>       h...@com.whateveryoursis.YourHandlerClass::handleJSONResponse(Lcom/

Adam T

unread,
Oct 7, 2008, 8:03:59 AM10/7/08
to Google Web Toolkit
mmm, your code below doesn't really follow the pattern in the link of
your first post - however, I think I know why your handle method is
calling. The code below seems to use a timer to loop around checking
if something has been returned, if not it fires your handler with null
- which explains why you handler is called and you only see null. I
assume that if you add the method name to your response you would
eventually get your handler called with a value.

I might misunderstand the code on the web site you link to, but I
personally would recommend using the code shown in Dan's article
(http://code.google.com/support/bin/answer.py?
answer=65632&topic=11368 ) for the client side. It works fine,
removes the need for a timer (which is going to take resource in your
application) and truly handles the asynchronous nature of the web.

//Adam

On 7 Okt, 13:46, eggsy84 <jimbob...@hotmail.com> wrote:
> I expect thats what I'm missing then!
>
> My calling method is as follows:
>
> public native static void getJson(int requestId, String url, CallModel
> handler)
> /*-{
>         var callback = "callback" + requestId;
>
>     var script = document.createElement("script");
>     script.setAttribute("src", url+callback);
>     script.setAttribute("type", "text/javascript");
>
>         window[callback] = function(jsonObj)
>     {
>         handl...@com.mypackage.MyClass::handleJsonResponse(Lcom/google/
> gwt/core/client/JavaScriptObject;)(jsonObj);
>         window[callback + "done"] = true;
>     }
>
>     // JSON download has 1-second timeout
>     setTimeout(function()
>     {
>         if (!window[callback + "done"])
>         {
>             handl...@com.mypackage.MyClass::handleJsonResponse(Lcom/

eggsy84

unread,
Oct 7, 2008, 8:10:49 AM10/7/08
to Google Web Toolkit
Hi Adam

Thank you for this insight - I'll use Dans code for my client side to
fix this and add the callback as a parameter!

Thank you for all your help! I'll let you know how I get on

Regards,

eggsy84

unread,
Oct 9, 2008, 3:38:48 PM10/9/08
to Google Web Toolkit
Hi Adam

I've finally worked it out and a lot of it is very much down to your
help!!

What I wasn't reading on Dan's article was a very important part of
his Python server, basically the line:

body = '%s(%s);' % (fun_name, file('json.js').read())

which as you quite rightly stated includes the damn function name and
is very important!!!

So I have taken your advice and implemented my client side code as per
Dans article:

============

public void makeCall(String number)
{
String serverURL = ClientGlobals.CALL_SERVICE_URL+"callback=";
String callbackName = reserveCallback();
setup(this, callbackName);
System.out.println(serverURL + callbackName);
System.out.println(callbackName);
addScript(callbackName, serverURL + callbackName);
}

public String reserveCallback()
{
while (true)
{
if (!callbacks.containsKey(new Integer(curIndex)))
{
callbacks.put(new Integer(curIndex), null);
return "__gwt_callback" + curIndex++;
}
}
}

/**
* <p>Adds the JSONP script to our widget so we can make XSS requests
baby</p>
*
* @param uniqueId The unique id of the call
* @param url The URL of our Request
*/
public void addScript(String uniqueId, String url)
{
Element e = DOM.createElement("script");
DOM.setElementAttribute(e, "language", "JavaScript");
DOM.setElementAttribute(e, "src", url);
scriptTags.put(uniqueId, e);
DOM.appendChild(RootPanel.get().getElement(), e);
}

public void handle(JavaScriptObject jso)
{
if( jso != null )
{
Window.alert("Woohoo JSO is not null it bloody worked");
}
}


/**
*
* <p>Sets up our Javascript cross site JSON call</p>
*
* @param model Handles our Cross Site JSON call
* @param callback
*/
public native static void setup(CallModel model, String callback)
/*-{
$wnd[callback] = function(someData)
{
model.@com.myPackage.MyCall::handle(Lcom/google/gwt/core/client/
JavaScriptObject;)(someData);
}
}-*/;


============

Then importantly in my server side I have used the callback name in my
JSON output as you quite rightly mention!! :)

protected void doPost(HttpServletRequest req, HttpServletResponse
resp) throws ServletException, IOException
{
String asyncCallback = req.getParameter("callback");
String output = asyncCallback+"([{color: \"red\",value: \"#f00\"}])";

resp.setContentType("text/javascript");
resp.addHeader("Pragma", "no-cache");
resp.setStatus(200);

PrintWriter out = resp.getWriter();
out.println(output);
}

And its not hitting my handler

Thank you very much for your help. I'm going to post a tutorial on my
blog http://eggsylife.blogspot.com and will credit help toward
yourself.

Eggsy

eggsy84

unread,
Oct 10, 2008, 9:42:32 AM10/10/08
to Google Web Toolkit
Adam,

I've wrote a very quick tutorial on GWT and JSONP with a J2EE
container.

Hope you dont mind I've mentioned you and thanked you for the help.

Its located at:

http://eggsylife.blogspot.com/2008/10/gwt-and-cross-site-jsonp-in-j2ee.html


On Oct 9, 8:38 pm, eggsy84 <jimbob...@hotmail.com> wrote:
> Hi Adam
>
>         mod...@com.myPackage.MyCall::handle(Lcom/google/gwt/core/client/
> JavaScriptObject;)(someData);
>     }
>   }-*/;
>
> ============
>
> Then importantly in my server side I have used the callback name in my
> JSON output as you quite rightly mention!! :)
>
> protected void doPost(HttpServletRequest req, HttpServletResponse
> resp) throws ServletException, IOException
> {
>         String asyncCallback = req.getParameter("callback");
>         String output = asyncCallback+"([{color: \"red\",value: \"#f00\"}])";
>
>         resp.setContentType("text/javascript");
>         resp.addHeader("Pragma", "no-cache");
>         resp.setStatus(200);
>
>         PrintWriter out = resp.getWriter();
>         out.println(output);
>
> }
>
> And its not hitting my handler
>
> Thank you very much for your help. I'm going to post a tutorial on my
> bloghttp://eggsylife.blogspot.comand will credit help toward
> ...
>
> read more »
Reply all
Reply to author
Forward
0 new messages