When you call any "async" method in GWT, you can think of it as starting in
another Thread. This is the plain Java equivalent to your XmlParserUtil class:
public class XmlParserUtil {
String url;
Document doc;
public RequestBuilder requestBuilder;
String xmlStr;
public XmlParserUtil(String xmlFileName) {
this.url = xmlFileName;
}
public Document getDocument() {
Thread runner = new Thread(new Runnable() {
public void run() {
// I'm changing indent here for readability
try {
// This little bit in the RequestBuilder
URL javaUrl = new URL(XmlParserUtil.this);
InputStream in = javaUrl.openStream();
Reader isr = new InputStreamRead(in);
StringBuilder builder = new StringBuilder();
char[] buffer = new char[1024];
int bufferSize = 0;
// read in the String data from the URL
while((size = isr.read(buffer)) != -1) {
builder.append(buffer, 0, size);
}
isr.close();
in.close();
xmlStr = builder.toString();
// this is a GWT call
doc = XmlParser.parse(xmlStr);
} catch(IOException ioe) {
// this would be your onFailure method
GWT.log(ex.getMessage(), ex);
}
}
});
runner.start();
// do you see the problem here?
return doc;
}
}
So the solution to this problem is: any method that at any level calls an async
method cannot return anything from that async call! Thus we pass in a Callback
object of some sort. Then, once the data has been fetched, you can invoke the
Callback with the data you retrieved.
For a really simple way of fetching data into properties, take a look at my
blog
here:
http://lemnik.wordpress.com/2008/07/22/easy-property-binding-and-aync-callbacks-in-gwt/
and here (for an example of the usage):
http://lemnik.wordpress.com/2008/11/11/using-propertychangelisteners-to-ease-gwt-async-calls/
Again, these deal with RPC, but the principal is the same for a RequestBuilder.
gregor wrote:
> Hi Mives29,
>
> Jason's solution is correct. If you have TreeX implement his Callback
> interface, then XmlParserUtil will call TreeX.callback() when it has
> retrieved and parsed the XML document, so you move the tree building
> code (getTreeitem()) to start from TreeX.callback(String XML). This
> will solve you immediate problem, but the design cost is that
> XmlParserUtil can _only_ pass XML back to client widgets via that
> route. That is not necessarily a good design decision in the GWT
> asynchronous world.
>
> Because remote calls have so much effect on the user experience
> (network latency, deserialization etc) it is a good idea to keep as
> much flexibility as possible in how you sequence remote calls and
> widget construction. If you abandoned your idea of a reusable all in
> one XML fetcher/parser utility, you would have other ways to build
> this tree, e.g.
>
> 1) Make the XML fetch call and parse the XML in onModuleLoad() up
> front before you build the Tree and supply the imgUri's to the Tree's
> constructor
> 2) Make the fetch call from the Tree's constructor, and in
> onResponseReceived(..) parse the returned XML then build the
> Treeitems.
>
> Since you would normally want to make the user experience as smooth
> and uninterrupted as possible, it is good to maximize flexibility as
> to where and when you make remote calls. That means you can move them
> around to make improvements - it all depends on your particular
> application and your users. For example, many people pre-fetch all
> necessary data to build their top most layer of widgets and display an
> "Application Loading..." type message whilst this is going on. This
> might be good for a complex business application where users will
> typically start it up in the morning and read their mail whilst its
> loading. But in GMail Google don't do that: they pre-fetch the inbox
> list, but they build the little contacts widget on the left separately
> as you watch.
>
> The point is it's not always immediately obvious what the "best" way
> to sequence remote calls and widget construction is at first - I think
> being able to try out different approaches is an important design goal
> in GWT apps, maybe more so than making something like XmlParserUtil
> reusable. The user's convenience is more important than yours.
>
> regards
> gregor
>
>
>
> On Nov 11, 6:56 am, mives29 <[EMAIL PROTECTED]> wrote:
>> Hi! I've just re-read your reply, but I cant understand how the
>> interface would affect the program, and how would I use that new
>> getDocument() method of the XmlUtilParser class. Could you provide
>> another example (a working one, if possible)?
>>
>> This whole AsyncCallback/asynchronous stuff of GWT is driving me nuts.
>> I've created the same parser using a service, and Java's own DOM
>> parser, and it works already, however I really want to develop this
>> app of mine full GWT-style. Hope you could help me again, thanks.
>>
>> On Nov 10, 5:58 pm, Jason Morris <[EMAIL PROTECTED]> wrote:
>>
>>> Heya,
>>> Your basic problem here is that you can't have your XmlParserUtil return a
>>> Document object. The doc field will only be filled in when the async
>>> RequestBuilder request returns and the parser is finished with the XML
>>> string.
>>> By that time, your method has already returned.
>>> The only way to solve this is to create something like a:
>>> public interface Callback<V> {
>>> void callback(V value);
>>> }
>>> and have the XmlParserUtil look something more like this:
>>> public class XmlParserUtil {
>>> String url;
>>> Document doc;
>>> public RequestBuilder requestBuilder;
>>> String xmlStr;
>>> public XmlParserUtil(String xmlFileName) {
>>> this.url = xmlFileName;
>>> }
>>> public void getDocument(Callback<Document> callback) {
>>> if(doc != null) {
>>> callback.callback(doc);
>>> return;
>>> }
>>> try {
>>> requestBuilder = new
>>> RequestBuilder(RequestBuilder.GET,url);
>>> requestBuilder.sendRequest(null,new
>>> RequestCallback()
>>> {public void onResponseReceived(Request arg0,
>>> Response arg1) {
>>> xmlStr = arg1.getText();
>>> callback.callback(doc =
>>> XMLParser.parse(xmlStr));
>>> }
>>> public void onError(Request request, Throwable
>>> exception) {
>>> }});
>>> }
>>> catch (RequestException ex) {
>>> GWT.log(ex.getMessage(), ex);
>>> }
>>> }
>>> }
>>> For more information about async problems, it's related to RPC, but
>>> applicable
>>> to the RequestBuilder as well:
>>> http://lemnik.wordpress.com/2008/07/04/gwt-rpc-is-called-aynchronous-...
>>> mives29 wrote:
>>>> okay, let me be clear on this, again, my code. (now posting full
>>>> reworked source code)
>>>> // A composite w/c contains a tree.
>>>> public class TreeX extends Composite {
>>>> Document doc;
>>>> Tree t = new Tree();
>>>> int imgUris;
>>>> XmlParserUtil xmlParser;
>>>> public TreePol() {
>>>> TreeItem tItem = getTreeItem();
>>>> t.addItem(tItem);
>>>> initWidget(t);
>>>> }
>>>> private TreeItem getTreeItem() {
>>>> doc = getDoc();
>>>> imgUris = doc.getElementsByTagName("Image").getLength();
>>>> TreeItem root = null;
>>>> root = new TreeItem("Image Names");
>>>> for (int i=0;i<imgUris;i++) {
>>>> String tmpImgUri =
>>>> doc.getElementsByTagName("Image").item(i).getChildNodes().item(1).toString();
>>>> root.addItem(tmpImgUri);
>>>> }
>>>> return root;
>>>> }
>>>> public Document getDoc() {
>>>> xmlParser = new XmlParserUtil("image_uris.xml");
>>>> while (doc==null) {
>>>> doc = xmlParser.getDocument();
>>>> }
>>>> return doc;
>>>> }
>>>> }
>>>> //A class that i want to use as a utility class for parsing xml
>>>> documents. intended to be reusable
>>>> public class XmlParserUtil {
>>>> String url;
>>>> Document doc;
>>>> public RequestBuilder requestBuilder;
>>>> String xmlStr;
>>>> public XmlParserUtil(String xmlFileName) {
>>>> this.url = xmlFileName;
>>>> }
>>>> public Document getDocument() {
>>>> try {
>>>> requestBuilder = new
>>>> RequestBuilder(RequestBuilder.GET,url);
>>>> requestBuilder.sendRequest(null,new RequestCallback()
>>>> {public void onResponseReceived(Request arg0, Response
>>>> arg1) {
>>>> xmlStr = arg1.getText();
>>>> doc = XMLParser.parse(xmlStr);
>>>> }
>>>> public void onError(Request request, Throwable
>>>> exception) {
>>>> }});
>>>> }
>>>> catch (RequestException ex) {
>>>> GWT.log(ex.getMessage(), ex);
>>>> }
>>>> return doc;
>>>> }
>>>> }
>>>> //my entrypoint's onModuleLoad(), which adds the various custom
>>>> widgets i made to a tab panel
>>>> public void onModuleLoad() {
>>>> RichTextAreaX1 rtaPol = new RichTextAreaX1();
>>>> PictureViewer imgBrowser = new PictureViewer();
>>>> TreeX tree = new TreeX();
>>>> TabPanel tp = new TabPanel();
>>>> tp.add(tree, "1");
>>>> tp.add(rtaPol, "2");
>>>> tp.add(imgBrowser, "3");
>>>> tp.selectTab(0);
>>>> tp.setWidth("100%");
>>>> RootPanel.get("leftpanel").add(tp);
>>>> }
>>>> as you can see, I intend the XmlParserUtil class to be reusable. If I
>>>> place there various logic, then it would be out of scope for that
>>>> classs, because I intend to use XmlParserUtil just to parse an XML
>>>> file, and bring me back a parsed Document object. now how would I do
>>>> that if XmlParserUtil, which contains the requestbuilder and callback,
>>>> would be used by another class (which is a composite).
>>>> im new to this async thing, more so the requestbuilder stuff. thank
>>>> you for replying, i hope you could answer my question.
>>>> On Nov 10, 3:44 pm, "alex.d" <[EMAIL PROTECTED]> wrote:
>>>>> You have to accept the asynchronous nature of RPC-Requests in GWT and
>>>>> stop trying to build smth. synchronous around them. This code:
>>>>> -----------------------------------
>>>>> doc = getDoc(); //(where doc is a Document object)
>>>>> String subject =
>>>>> doc.getElementsByTagName("Image").item(0).getChildNodes().item(1).toString();
>>>>> String subject2 =
>>>>> doc.getElementsByTagName("Image").item(1).getFirstChild().getNodeValue();
>>>>> -----------------------------------
>>>>> should not be executed in the onload-routine but in the
>>>>> onResponseReceived()-method (or in any other procedure that you call
>>>>> out of it).
>>>>> On 10 Nov., 07:12, mives29 <[EMAIL PROTECTED]> wrote:
>>>>>> hi all. ive re-done my code just a while ago(been a busy week) and my
>>>>>> code kinda bothers me. here's the code re-done:
>>>>>> Entrypoint onModuleLoad() method:
>>>>>> doc = getDoc(); //(where doc is a Document object)
>>>>>> String subject =
>>>>>> doc.getElementsByTagName("Image").item(0).getChildNodes().item(1).toString();
>>>>>> String subject2 =
>>>>>> doc.getElementsByTagName("Image").item(1).getFirstChild().getNodeValue();
>>>>>> Entrypoiny getDoc() method:
>>>>>> public Document getDoc() {
>>>>>> XmlParserUtil xmlParser = new XmlParserUtil("image_uris.xml");
>>>>>> int i = 0;
>>>>>> while (doc==null) {
>>>>>> doc = xmlParser.getDocument();
>>>>>> }
>>>>>> return doc;
>>>>>> }
>>>>>> here's the service implementation:
>>>>>> public class XmlParserUtil {
>>>>>> String url;
>>>>>> Document doc;
>>>>>> RequestBuilder requestBuilder;
>>>>>> public String xmlStr;
>>>>>> public XmlParserUtil(String xmlFileName) {
>>>>>> this.url = xmlFileName;
>>>>>> }
>>>>>> public Document getDocument() {
>>>>>> RequestCallback xmlRcb = new RequestCallback()
>>>>>> {public void onResponseReceived(Request arg0, Response
>>>>>> arg1) {
>>>>>> xmlStr = arg1.getText();
>>>>>> doc = XMLParser.parse(xmlStr);
>>>>>> }
>>>>>> public void onError(Request request, Throwable
>>>>>> exception) {
>>>>>> // TODO Auto-generated method stub
>>>>>> }};
>>>>>> try {
>>>>>> requestBuilder = new
>>>>>> RequestBuilder(RequestBuilder.GET,url);
>>>>>> requestBuilder.sendRequest(null,xmlRcb);
>>>>>> }
>>>>>> catch (RequestException ex) {
>>>>>> GWT.log(ex.getMessage(), ex);
>>>>>> }
>>>>>> return doc;
>>>>>> }
>>>>>> }
>>>>>> my problem is on the Entrypoint logic. you see the getDoc() method
>>>>>> there?
>>>>>> it has this:
>>>>>> while (doc==null) {
>>>>>> doc = xmlParser.getDocument();
>>>>>> }
>>>>>> which just loops around until the service implementation has already
>>>>>> populated the parsed document.
>>>>>> what bothers me with this code is that it
>> ...
>>
>> read more ยป
> >
>
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Google Web Toolkit" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/Google-Web-Toolkit?hl=en
-~----------~----~----~----~------~----~------~--~---