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.
Thank you.
++
mcben
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?
Axel
> Its contractid is like
> "@mozilla.org/rdf/datasource;1?name=mydatasource" so as to get it in
> the chrome with the "rdf:mydatasource" keyword.
Technically it's not necessary to register it in this case, and in fact
you can crash if you don't register it correctly, so I'd advise you to
avoid that.
--
Warning: May contain traces of nuts.
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.
On 4 fév, 12:59, Neil <n...@parkwaycc.co.uk> wrote:
> mcben wrote:
> > Its contractid is like
> > "@mozilla.org/rdf/datasource;1?name=mydatasource" so as to get it in
> > the chrome with the "rdf:mydatasource" keyword.
>
> Technically it's not necessary to register it in this case, and in fact
> you can crash if you don't register it correctly, so I'd advise you to
> avoid that.
The case you explain is the "rdf:mydatasource" keywork trick ?
Related to my other reply to Axel, i had managed to register a in-
memory datasource, the same way i tried to with an xml datasource.
But anyway, after a few test, it seems that registration is indeed not
required.
I suspect that something in your implementation is broken. There is
http://developer.mozilla.org/en/docs/Aggregating_the_In-Memory_Datasource,
but that hasn't been maintained in years, same goes for its sibling
documents.
Sorry, no better idea, maybe if you post parts of your code there's
better chances.
Axel
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 ?
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?
But I guess for real questions on this, you should try .js-engine.
Axel
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.
I left all the context in, I guess it's best to read from the bottom up
for those in js-engine.
Axel
I'm back :)
In fact, I already try to aggregate an in-memory datasource, that was
not with xml datasource.
See here my post (without replies :[) to .xpcom:
http://groups.google.com/group/mozilla.dev.tech.xpcom/browse_frm/thread/38aeabcf8bfef28c/6d12b03fa5010e7b?lnk=st&q=Javascript%2C+RDF+in-mem+datasource+and+XPCOM+Aggregation#6d12b03fa5010e7b
My new tries is same as previous ones of today :
in the chrome, i cant QI to another interface the datasource that i
get from RDFService.GetDataSource(), nor if i directly call my component
with Components.classes.
The asked interface is not forwarded to the outter object, eg: my own
component.
Here is the code:
function MyDataSourceImplementation(struct)
{
this.mInner = Components
.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]
.createInstance(Components.interfaces.nsIRDFDataSource,
this,
Components.interfaces.nsISupports);
}
MyDataSourceImplementation.prototype = {
mInner:null,
QueryInterface : function MDSI_QueryInterface(iid) {
if(iid.equals(Components.interfaces.nsIRDFDataSource))
return this.mInner;
if (iid.equals(Components.interfaces.nsIDOMEventListener) ||
iid.equals(Components.interfaces.nsIRDFRemoteDataSource) ||
iid.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
}
}
It'worse, if I replace the mInner initialization with :
this.mInner = Components
.manager
.createInstance(inMemDsCID,
this, Components.interfaces.nsISupports);
The datasource if filled, that's fine, but with some QI errors, but
crashses with a Segmentation fault 15 seconde later.
The QI also does not work.
I can't find any XPCOM aggregation with javascript example :(
Are my parameters right for createInstance ?
Thank you Axel.
And (far.. far..) below is my last reply (that i sent to .rdf before
the X-Post) with XPCOM aggregation with Javascript tries.
In fact, I already try to aggregate an in-memory datasource, that was
not with xml datasource.
See here my post (without replies :[) to .xpcom:
http://groups.google.com/group/mozilla.dev.tech.xpcom/browse_frm/thread/38aeabcf8bfef28c/6d12b03fa5010e7b?lnk=st&q=Javascript%2C+RDF+in-mem+datasource+and+XPCOM+Aggregation#6d12b03fa5010e7b
My new tries is same as previous ones of today :
in the chrome, i cant QI to another interface the datasource that i
get from RDFService.GetDataSource(), nor if i directly call my component
with Components.classes.
The asked interface is not forwarded to the outter object, eg: my own
component.
Here is the code:
function MyDataSourceImplementation(struct)
{
this.mInner = Components
> QueryInterface : function MDSI_QueryInterface(iid) {
> if (iid.equals(Components.interfaces.nsIRDFDataSource))
> return this.mInner;
>
> if (iid.equals(Components.interfaces.nsIDOMEventListener) ||
> iid.equals(Components.interfaces.nsIRDFRemoteDataSource) ||
> iid.equals(Components.interfaces.nsISupports))
> return this;
>
> throw Components.results.NS_ERROR_NO_INTERFACE;
> }
This won't work; any object you return from QueryInterface must return
the original object when queried back to nsISupports, which mInner
clearly won't do.
Anybody there that could help me ? :(
In fact, i never thought that i could set a xpcom instance as a
prototype of a js object.
But according to this thread :
http://groups.google.fr/group/mozilla.dev.tech.xpcom/browse_thread/thread/b44471fb71569447/e3146a7288feec1d?lnk=gst&q=inheritance#e3146a7288feec1d
And both last replies of Boris Zbarsky and Honza T_, it should works !
Quote :
"Just create an instance and set it as your prototype in your
constructor
function.... "
So how can i properly set it as my prototype in my constructor, and
then adds new methods and implements new interfaces ?