Re: Cannot call RegisterDataSource on a own datasource

mcben <[email protected]> Mon, 4 Feb 2008 07:42:59 -0800 (PST)
Newsgroups gmane.comp.mozilla.devel.rdf
Organization http://groups.google.com
Message-ID <195daffc-b6ef-4845-88f9-93d460fffdf4@c23g2000hsa.googlegroups.com>
On 4 fév, 15:47, Axel Hecht <[email protected]> wrote:
> mcben wrote:
> > On 4 fév, 14:52, Axel Hecht <[email protected]> wrote:
> >> mcben wrote:
> >>> On 4 fév, 09:27, Axel Hecht <[email protected]> wrote:
> >>>> mcben wrote:
> >>>>> Hi folks,
> >>>>> Here is quite a long post about a problem with a own datasource
> >>>>> implementation based on a xml-datasource.
> >>>>> I'm looking for any clues or links that could help me to achieve my
> >>>>> first goal : a custom datasource that i could fill from a CSV file.
> >>>>> Thanks in advance for reading me :)
> >>>>> I'm creating a javascript component that fetches a CSV file with
> >>>>> XMLHttpRequest, parses it, then adds lines as RDF resources in a RDF
> >>>>> datasource.
> >>>>> Its contractid is like "@mozilla.org/rdf/datasource;1?name=mydatasource"
> >>>>> so as to get it in the chrome with the "rdf:mydatasource" keyword.
> >>>>> I started this work by using an in-memory-datasource as the __proto__
> >>>>> properties of my javascript prototype.
> >>>>> I could fetch my file and appends data in the datasource : the result
> >>>>> was shown in a rdf tree.
> >>>>> The first problem with this design was that i was not able to overwrite
> >>>>> some nsIRDFDataSource methods (not a real problem in fact), nor extends
> >>>>> the in-memory-datasource component with other interfaces : the QI method
> >>>>> of my component was never called, so i got a NS_ERROR_NO_INTERFACE
> >>>>> error, when i QI another interface (eg: nsIDOMListener to adds
> >>>>> XMLHttpRequest callback in my chrome).
> >>>>> Then i took a look a the rdf:localstore C++ implementation
> >>>>> (nsLocalStore.cpp) and i'm quite happy with its design : it works like a
> >>>>> wrapper around a xml-datasource. It "justs" load data from
> >>>>> localstore.rdf in the profile directory or creates it if it does not exist.
> >>>>> Then with that, I could even read and store a cached version of my CSV
> >>>>> file on disk.
> >>>>> So, I've made the same design in my javascript component, all
> >>>>> nsIRDFDataSource related methods are forwarded to a "mInner"
> >>>>> xml-datasource property.
> >>>>> But when i try to call RegisterDataSource to register my datasource in
> >>>>> the constructor of my component, it throws a NS_ERROR_FAILURE :
> >>>>> Here is the code :
> >>>>> this._loadLocalData();
> >>>>> RDF.RegisterDataSource(this, false);
> >>>>> this._loadRemoteData();
> >>>>> If i log "this" the console service returns me that is an Object.
> >>>>> That's the problem ?
> >>>>> In fact I get from the chrome : [xpconnect wrapped (nsISupports,
> >>>>> nsIRDFDataSource, nsIRDFRemoteDataSource)] after the registration, even
> >>>>> if the registration fails.
> >>>>> But, the nsIRDFRemoteDatasource Init method is called at every calls
> >>>>> from chrome.
> >>>>> Maybe the real problem comes from the xml-datasource initialization.
> >>>>> The following code is called by the constructor of my component :
> >>>>> this.mInner = Components
> >>>>>        .classes["@mozilla.org/rdf/datasource;1?name=xml-datasource"]
> >>>>>        .createInstance(Components.interfaces.nsIRDFDataSource);
> >>>>> var remote = this.mInner
> >>>>>        .QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
> >>>>> remote.Init(filespec);
> >>>>> The "Init" call throws an exception with the following code
> >>>>> "0x804b000a". Its related with nsISocketTransport and means
> >>>>> STATUS_WAITING_FOR. Ok, why not.
> >>>>> Then remote.loaded returns false, and calling remote.Refresh(false)
> >>>>> throws a NS_ERROR_FAILURE
> >>>>> What is wrong with that ?
> >>>>> So i catch this Init exception and get my remote datasource with the RDF
> >>>>> Service like this :
> >>>>> remote = RDF.GetDataSource(filespec)
> >>>>>           .QueryInterface(Components.interfaces.nsIRDFRemoteDataSource)
> >>>>> this.mInner = remote
> >>>>>           .QueryInterface(Components.interfaces.nsIRDFDataSource);
> >>>>> In that case no problem with remote ds, but the RegisterDataSource go on
> >>>>> failing.
> >>>>> And later, in the chrome, when i call a method or property that is
> >>>>> forwarded to the inner, i got back a undefined result.
> >>>>> And i cant look any result in my xul tree.
> >>>>> I can post the full source code of the component if needed.
> >>>> I suspect that it has something to do with your move from an in-memory
> >>>> datasource as inner to an rdf/xml as inner. I didn't get why you made
> >>>> that move, was that intentional?
> >>> Hi Axel,
> >>> Thank you for your answer.
> >>> To begin, you can grab my component source code here, with the "second
> >>> design" implementation (with inner):
> >>>http://pastebin.mozilla.org/319272
> >>> And the xul tree that should show my results look quite simple :
> >>> <tree id="itemtypes-tree" flex="1"
> >>>         datasources="rdf:bi-itemtypes" ref="bi:itemtypes-list">
> >>>   <treecols>
> >>>     <treecol id="col-name" primary="true" label="Name" flex="1"/>
> >>>   </treecols>
> >>>   <template>
> >>>     <treechildren>
> >>>       <treeitem uri="rdf:*">
> >>>         <treerow>
> >>>           <treecell label="rdf:*" />
> >>>         </treerow>
> >>>       </treeitem>
> >>>     </treechildren>
> >>>   </template>
> >>> </tree>
> >>> Hum. yes, the move was partially intentional :
> >>>  * My first try with an in-memory datasource was with the instance set
> >>> as __proto__ property in the prototype of my component. That is to
> >>> say, that it was not with the "inner" design.
> >>> The main problem with this design was that i could not extends my
> >>> component by implementing other public interface : the QI function of
> >>> my component was never called, nor any method that i though they
> >>> overrode existing methods of the in-mem. The only thing that i could
> >>> improve in this design was the init phase of my component (by filling
> >>> in-mem with XMLHttpRequest) nothing else.
> >>> Is it normal that with the following code, the QueryInterface method
> >>> is never called, that's why i cant implement any other interfaces ? (i
> >>> think that the __proto__ QI is called instead).
> >>> function myComponentImplementation() {
> >>> }
> >>> myComponentImplementation.prototype {
> >>>     __proto__ : Components.classes["@mozilla.org/rdf/datasource;1?
> >>> name=in-memory-datasource"]
> >>>                          .createInstance(Components.interfaces.nsIRDFDataSource),
> >>>     QueryInterface : function(iid) {
> >>>         if (!iid.equals(C_i.nsIDOMEventListener) &&
> >>>             !iid.equals(C_i.nsIRDFDataSource) &&
> >>>             !iid.equals(C_i.nsIRDFRemoteDataSource) &&
> >>>             !iid.equals(C_i.nsISupports))
> >>>             throw Components.results.NS_ERROR_NO_INTERFACE;
> >>>         ConsoleService.logStringMessage("QI called:" + iid);
> >>>         return this;
> >>>     }
> >>> }
> >>>  * With the "inner" design, i immediatly use a xml-datasource, just
> >>> like the nsLocalStore implementation. With that, i could load a cached
> >>> version of the rdf file from disk, and then update it with
> >>> XMLHttpRequest only if it really needed.
> >>> Since, you are thinking that the problem can come from the xml-
> >>> datasource, i took the same component, and just replaced the xml-
> >>> datasource inner, with an in-memory-datasource.
> >>> Indeed, it seems there is no major failure with initialization, i can
> >>> register the datasource, but the content that i downloaded with
> >>> XMLHttpRequest and that i put in the ds is not reflected in my chrome
> >>> xul tree, nor i can query my datasource for any target that sould
> >>> exists.
> >>> More precisely, about any errors, i had to create the container (the
> >>> one to wich I append downloaded resources) with this.mInner (the in-
> >>> mem ds) and not my component. Is it normal ?
> >>> Look:
> >>> this._container = RDFContainer.MakeSeq(this.mInner,
> >>> RDF.GetResource(this._struct.ref));
> >>> Even though my component should implement nsIRDFDataSource, that's why
> >>> i thought i could do that :
> >>> this._container = RDFContainer.MakeSeq(this,
> >>> RDF.GetResource(this._struct.ref));
> >>> But it seems that append does not work anyway.
> >>> * Finally, why I made the move to xml-datasource ?
> >>> (I thought) I could parse a local rdf file at init, and flushes the
> >>> modified results at destruction. I even could observe the download
> >>> progress from chrome (eg: with a progressbar), etc.
> >>> So any clue ?
> >>> What do you think is the best implementation ?
> >>> The first design could be perfect if i could override and extends
> >>> methods and interfaces of the __proto__ instance.
> >>> Thank you for reading me.
> >> I suspect that something in your implementation is broken. There ishttp://developer.mozilla.org/en/docs/Aggregating_the_In-Memory_Dataso...,
> >> but that hasn't been maintained in years, same goes for its sibling
> >> documents.
>
> > Yes, i know this document, and tried some months ago to agregate a xml-
> > datasource (for another project) and gave up when i saw that xml-
> > datasource cant be aggregated ;-)
>
> > Maybe i should give another try with the in-mem datasource.
>
> > But can you confirm me that i could try that with a javascript
> > implementation ?
>
> >> Sorry, no better idea, maybe if you post parts of your code there's
> >> better chances.
>
> > Héhé. The whole component is too long / time consuming for you to take
> > a look ?
> > I understand.
>
> > So first question, that i previously asked. WIth a component
> > implementation below, is it normal that I cant override public methods
> > of the __proto__. In fact, I can only add private method, that cant be
> > accessed by the chrome ?
>
> > function myComponentImplementation() {
> > }
>
> > myComponentImplementation.prototype {
> >     __proto__ : Components.classes["@mozilla.org/rdf/datasource;1?
> > name=in-memory-datasource"]
> >                          .createInstance(Components.interfaces.nsIRDFDataSource),
>
> >     QueryInterface : function(iid) {
> >         if (!iid.equals(C_i.nsIDOMEventListener) &&
> >             !iid.equals(C_i.nsIRDFDataSource) &&
> >             !iid.equals(C_i.nsIRDFRemoteDataSource) &&
> >             !iid.equals(C_i.nsISupports))
> >             throw Components.results.NS_ERROR_NO_INTERFACE;
> >         ConsoleService.logStringMessage("QI called:" + iid);
> >         return this;
> >     }
>
> > }
>
> That is more of a js/xpconnect question. Trying
>
> function A(){this.bar=3;}
> function B(){this.__proto__ = new A();this.foo=6};
> b=new B()
> [object Object]
> props(b)
> Fields: foo
> Fields of prototype: bar
> Methods of prototype of prototype of prototype: extend
> b.bar
> 3
> b.foo
> 6
>
> makes me think that you should set the __proto__ to a new instance of
> the in-mem ds in the Constructor. Which makes sense, as you don't want
> to share the in-mem ds across all instances of
> myComponentImplementation, right?

Good point.

I just tried that :

function myDataSourceImplementation(struct)
{
    this.__proto__ = Components.classes["@mozilla.org/rdf/datasource;1?
name=in-memory-datasource"]
                         .createInstance(Components.interfaces.nsIRDFDataSource);

    this.init = function() {
       // load data with XMLHttpRequest
    };

   this.QueryInterface = function(iid) {
        ConsoleService.logStringMessage("QI called:" + iid);
        if (!iid.equals(Components.interfaces.nsIDOMEventListener) &&
            !iid.equals(Components.interfaces.nsIRDFDataSource) &&
            !iid.equals(Components.interfaces.nsIRDFRemoteDataSource)
&&
            !iid.equals(Components.interfaces.nsISupports))
            throw Components.results.NS_ERROR_NO_INTERFACE;
        return this;
    };

    this.init();

}

As you can see, i'm compelled to binds new methods in the constructor
since, the prototype is overriden by "this.__proto__". Not a real
problem.
Code works (i see the data in the chrome).

But my new QI is never called, like before, and prevents me from
implementing other interfaces :-(

>
> But I guess for real questions on this, you should try .js-engine.

Maybe.
This evening, I'll try XPCOM aggregation (like in "Aggregating In
Memory Datasource"), and will ask .js-engine for any clues.
I'll post back here any progress about this subject.

Thanks for your time.