IXMLDOMDocument - how to use class with async #loadURL:

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

IXMLDOMDocument - how to use class with async #loadURL:

Günther Schmidt
Hi,

how can I have code executed after anIXMLDOMDocument has successfully loaded?

I've been using

|doc|

doc := IXMLDOMDocument new.
doc async: false.
doc loadURL:'some.url.com'.
root := doc documentElement.


in a method, but will only work when I do "doc async: false".

Would this be foreign function callback, event trigger or something else. Looking at the class doesn't give me any clues.

Günther


Reply | Threaded
Open this post in threaded view
|

Re: IXMLDOMDocument - how to use class with async #loadURL:

Bernhard Kohlhaas-7
Guenther,

I assume that you want that execution done asynchonously. I'm not
familiar with IXMLDOMDocument, so I don't know, what special mechanisms
that class provides, but have you considered executing the whole block
asynchronously, i.e.

[ |doc|
   doc := IXMLDOMDocument new.
   doc async: false.
   doc loadURL:'some.url.com'.
   root := doc documentElement ] fork.

If your coding depends on a certain variable being available at a
particular time you might have to use a semaphore though, or perhaps
instead of using #fork, you might be able to use a DeferredValue, which
takes care of the semaphore handling for you.

Bernhard


Günther Schmidt wrote:

> Hi,
>
> how can I have code executed after anIXMLDOMDocument has successfully
> loaded?
>
> I've been using
>
> |doc|
>
> doc := IXMLDOMDocument new.
> doc async: false.
> doc loadURL:'some.url.com'.
> root := doc documentElement.
>
>
> in a method, but will only work when I do "doc async: false".
>
> Would this be foreign function callback, event trigger or something
> else. Looking at the class doesn't give me any clues.
>
> Günther


Reply | Threaded
Open this post in threaded view
|

Re: IXMLDOMDocument - how to use class with async #loadURL:

Günther Schmidt
Dear Bernhard,

Bernhard Kohlhaas wrote:

> Guenther,
>
> I assume that you want that execution done asynchonously. I'm not
> familiar with IXMLDOMDocument, so I don't know, what special mechanisms
> that class provides, but have you considered executing the whole block
> asynchronously, i.e.
>
> [ |doc|
>   doc := IXMLDOMDocument new.
>   doc async: false.
>   doc loadURL:'some.url.com'.
>   root := doc documentElement ] fork.
>
> If your coding depends on a certain variable being available at a
> particular time you might have to use a semaphore though, or perhaps
> instead of using #fork, you might be able to use a DeferredValue, which
> takes care of the semaphore handling for you.
>

How would I do either (using semaphore or DeferredValue)? This is entirely new ground for me.

Günther


Reply | Threaded
Open this post in threaded view
|

Re: IXMLDOMDocument - how to use class with async #loadURL:

Chris Uppal-3
In reply to this post by Günther Schmidt
Günther,

> how can I have code executed after anIXMLDOMDocument has successfully
> loaded?

I assume that you want to use asynchronous loading so that your application
doesn't freeze while it's waiting for the download to complete ?  If not then
the following may not make much sense, or may be wrong...

There seem to be at least two techniques (found by assiduous Googling, since I
didn't know this stuff either...).

Given:

    url := 'whatever.xml'.
    doc := IXMLDOMDocument new.

One simple technique is just to pump Windows messages while you are waiting for
the document's #readyState to become 4:

    doc loadURL: url.
    [doc readyState = 4] whileFalse: [SessionManager inputState pumpMessages].
    doc documentElement inspect.

That will process Windows messages "in-line", so your application will continue
to respond to user-input while the #whileFalse: loop is ongoing.  You'd
probably need to set some flags to say that a download is underway, or else you
might end up allowing the user to start another download while the first was
still incomplete.

A more complex way (that I admit I don't fully understand -- so I hope that
someone will point out any errors I've made), is to use an AXEventSink to
translate the "onreadystatechange" notifications into normal Dolphin events.
That's a little tricky to illustrate in with code you can run in a workspace,
but here's a nasty hack to set up an example event handler (real code would
look nothing like this because you'd be able to use normal methods, rather than
sending #value to a Block).

    target := Object new.
    handler := [(doc readyState = 4) ifTrue: [doc documentElement inspect]].
    target when: #onreadystatechange send: #value to: handler.

With that hack out of the way, we can now utter the mystical incantation that
arranges for the #onreadystatechange event to be triggered off the target
object:

    typeInfo := doc coclassTypeInfo defaultSourceInterface.
    sink := AXEventSink target: target sourceTypeInfo: typeInfo.
    sink connect: doc.

and now we can start loading the URL asynchronously:

    doc loadURL: url.

and the inspector should open once the download is complete.

If you had the equivalent code (without the hack) in your application, then
once you had executed the last line, your app would return to its normal
event-driven mode, waiting for the next user-input and reacting to that as it
happened.  Also, at some later time, the event would be triggered and your
handler code then would be executed.  As with the #pumpMessages approach, you'
might want to set some sort of flag to say that you were expecting a download
to complete sooner or later, and use that to stop the user starting another
download before the first had completed.

BTW, if you do use flags like that, don't forget to test what happens when the
download fails halfway, since you wouldn't want the flag to remain set in that
case.  (I have no idea how a IXMLDOMDocument behaves when a download fails like
that, so I can't suggest a suitable way of handling it.)

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: IXMLDOMDocument - how to use class with async #loadURL:

Günther Schmidt
Chris,

Chris Uppal wrote:

> Günther,
>
>
>>how can I have code executed after anIXMLDOMDocument has successfully
>>loaded?
>
>
> I assume that you want to use asynchronous loading so that your application
> doesn't freeze while it's waiting for the download to complete ?  If not then
> the following may not make much sense, or may be wrong...

you're exactly right. Even if I put this into a "fork", which is what I've done first, the UI freezes.

>
> There seem to be at least two techniques (found by assiduous Googling, since I
> didn't know this stuff either...).
>
> Given:
>
>     url := 'whatever.xml'.
>     doc := IXMLDOMDocument new.
>
> One simple technique is just to pump Windows messages while you are waiting for
> the document's #readyState to become 4:
>
>     doc loadURL: url.
>     [doc readyState = 4] whileFalse: [SessionManager inputState pumpMessages].
>     doc documentElement inspect.
>
> That will process Windows messages "in-line", so your application will continue
> to respond to user-input while the #whileFalse: loop is ongoing.  You'd
> probably need to set some flags to say that a download is underway, or else you
> might end up allowing the user to start another download while the first was
> still incomplete.
>
> A more complex way (that I admit I don't fully understand -- so I hope that
> someone will point out any errors I've made), is to use an AXEventSink to
> translate the "onreadystatechange" notifications into normal Dolphin events.
> That's a little tricky to illustrate in with code you can run in a workspace,
> but here's a nasty hack to set up an example event handler (real code would
> look nothing like this because you'd be able to use normal methods, rather than
> sending #value to a Block).
>
>     target := Object new.
>     handler := [(doc readyState = 4) ifTrue: [doc documentElement inspect]].
>     target when: #onreadystatechange send: #value to: handler.
>

Chris if this works, that would be the best solution (I think). I already thought there would have to be some sort of Event meachanism but I couldn't find out what signals would be triggered.


> With that hack out of the way, we can now utter the mystical incantation that
> arranges for the #onreadystatechange event to be triggered off the target
> object:
>
>     typeInfo := doc coclassTypeInfo defaultSourceInterface.
>     sink := AXEventSink target: target sourceTypeInfo: typeInfo.
>     sink connect: doc.
>
> and now we can start loading the URL asynchronously:
>
>     doc loadURL: url.
>
> and the inspector should open once the download is complete.
>
> If you had the equivalent code (without the hack) in your application, then
> once you had executed the last line, your app would return to its normal
> event-driven mode, waiting for the next user-input and reacting to that as it
> happened.  Also, at some later time, the event would be triggered and your
> handler code then would be executed.  As with the #pumpMessages approach, you'
> might want to set some sort of flag to say that you were expecting a download
> to complete sooner or later, and use that to stop the user starting another
> download before the first had completed.
>
> BTW, if you do use flags like that, don't forget to test what happens when the
> download fails halfway, since you wouldn't want the flag to remain set in that
> case.  (I have no idea how a IXMLDOMDocument behaves when a download fails like
> that, so I can't suggest a suitable way of handling it.)
>
>     -- chris
>
>

All in all I'm surprised that there are no previous postings relating to this particular problem. I had thought a lot of people are use XML one way or the other.

Thanks

Günther


Reply | Threaded
Open this post in threaded view
|

Re: IXMLDOMDocument - how to use class with async #loadURL:

Bernhard Kohlhaas-7
In reply to this post by Günther Schmidt
Guenther Schmidt wrote:

[...]
> How would I do either (using semaphore or DeferredValue)? This is
> entirely new ground for me.

I wasn't aware that the loading would block the entire UI. So my
suggestions do not apply here, so just forget about them and follow
Chris' suggestion.

Sorry for the confusion,

Bernhard