Jun 04
Gwt-Ext Custom Reader
If I had to populate a Gwt-Ext store with data in arbitrary format,
- I could parse data on the client and use a MemoryProxy + ArrayReader or
- I could have the server do the parsing and send JSON data to the client which could then be handled by a JsonReader for populating the store.
This thread at Gwt-Ext.com made me wonder if a good third option would be to have a reader that can be used directly with the store and proxy mechanism and that allows one to write parsing logic in Java on the client. I have attempted a CustomReader that does just that. You can use it with a proxy that fetches remote data.
CustomReader registers an Ext user extension of the same name. Ill refer to them as JavaCustomReader and JSCustomReader to avoid confusion. JavaCustomReader has a static method for registering the user extension.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ... static { init(); } ... private static native void init()/*-{ $wnd.Ext.namespace("GwtExt"); $wnd.GwtExt.CustomReader = function(meta, recordType, readerInstance) { meta = meta || {}; this.readerInstance = readerInstance; $wnd.GwtExt.CustomReader.superclass.constructor.call(this); }; $wnd.Ext.extend($wnd.GwtExt.CustomReader, $wnd.Ext.data.DataReader, { read : function(response){ return this.readerInstance.@com.maharana.gwtextcustomreader.client.CustomReader::read(Lcom/google/gwt/core/client/JavaScriptObject;)(response); }, readRecords : function(o){ alert('Custom reader does not work with local data.'); } }); }-*/; |
JSCustomReader is passed one additional parameter when it is instantiated. This parameter is a reference to the JavaCustomReader that created it.
1 2 3 | protected native JavaScriptObject create(JavaScriptObject config, JavaScriptObject recordDef)/*-{ return new $wnd.GwtExt.CustomReader(config, recordDef, this); }-*/; |
JSCustomReader’s read() method is called by a proxy which has finished loading remote data. The proxy expects a return object with the success status, total record count and actual records as its fields. JSCustomReader invokes JavaCustomReader.read() which looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public JavaScriptObject read(JavaScriptObject response) { ReaderResult r = handleRemoteResponse(new RemoteResponse(response)); JavaScriptObject o = JavaScriptObjectHelper.createObject(); JavaScriptObjectHelper.setAttribute(o, "success", r.success); JavaScriptObject[] jsRecords = new JavaScriptObject[r.records.length]; for(int i =0; i<r.records.length;i++) jsRecords[i] = r.records[i].getJsObj(); JavaScriptObjectHelper.setAttribute(o, "records", JavaScriptObjectHelper.convertToJavaScriptArray(jsRecords)); JavaScriptObjectHelper.setAttribute(o, "totalRecords", r.records.length); return o; } // subclass and put your parsing logic here public abstract ReaderResult handleRemoteResponse(RemoteResponse remoteResponse); |
In turn, it handles the to/from JavaScriptObject stuff and calls handleRemoteResponse(). Users just need to provide their parsing code in this method in derived classes. RemoteResponse and ReaderResult are convenience classes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class RemoteResponse { JavaScriptObject response; public RemoteResponse(JavaScriptObject response) { this.response = response; } public String getResponseText() { return JavaScriptObjectHelper.getAttribute(response, "responseText"); } public String getResponseXML() { return JavaScriptObjectHelper.getAttribute(response, "responseXML"); } } class ReaderResult { public final boolean success; public final Record[] records; public ReaderResult(boolean success, Record[] records) { this.success = success; this.records = records; } } |
This completes CustomReader. As an example, I made a simple CSV parsing reader as mentioned in that thread at Gwt-Ext.com. This reader gets stock quotes from Yahoo finance as a CSV file and creates records out of them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | RecordDef recordDef = new RecordDef( new FieldDef[]{ new StringFieldDef("name"), new StringFieldDef("lasttrdate"), new StringFieldDef("lasttrval") } ); CustomReader c = new CustomReader(recordDef,1){ public ReaderResult handleRemoteResponse(RemoteResponse remoteResponse) { String csvalue = remoteResponse.getResponseText(); String[] lines = csvalue.split("\n"); Record[] records = new Record[lines.length]; for(int i=0; i<lines.length; ++i) { String[] columns = lines[i]. replace('\"', ' ').split(","); records[i] = this.recordDef.createRecord(columns); } return new ReaderResult(true, records); } }; |
For getting the file from Yahoo, I made a utility Servlet Redirect.java which fetches the contents of a url and passes it to the client.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String queryString = request.getQueryString(); String address = queryString.split("url=")[1]; URL url = new URL(address); PrintStream out = new PrintStream(response.getOutputStream()); String inputLine = ""; BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); while((inputLine = br.readLine()) != null) { // System.out.println(inputLine); out.println(inputLine); } out.close(); } |
It is invoked by an HttpProxy on the client as follows:
1 2 3 4 5 6 | ... String url = "http://download.finance.yahoo.com/d/quotes.csv?s=INFY+XOM+BBDb.TO+JNJ+MSFT+GOOG+YHOO&f=nd1l1"; DataProxy proxy = new HttpProxy("redirect?url=" + url); Store store = new Store(proxy, c); // CustomReader c from above ... |
Once we have the utility servlet and CustomReader as part of the project, I think it will be relatively easier to populate stores with any arbitrary format data. Feel free to drop your comments / suggestions. I would love to hear them.
Download Eclipse project from Gwt-Ext.com or Rapidshare
Reference: GwtProxy by michal.bergmann

June 6th, 2008 at 2:09 pm
Hi, maharana.
Recently, i used GWT-UX module. But I
found out using Firebug that there are some JavaScript errors:
Ext is not defined
[Break on this error] Ext.ux.Multiselect = Ext.extend(Ext.form.Field,
{
Multiselect.js (line 3)
Ext is not defined
[Break on this error] Ext.namespace(”Ext.ux”);
DDView.js (line 5)
Ext is not defined
[Break on this error] Ext.ux.MultiMonthCalendar =
Ext.extend(Ext.Component, {
MultiMonthCalenda… (line 16)
Ext is not defined
[Break on this error] Ext.namespace(’Ext.ux.grid’);
BufferedGridDragZ… (line 14)
Ext is not defined
[Break on this error] Ext.namespace(’Ext.ux’);
BufferedGridToolb… (line 21)
Ext is not defined
[Break on this error] Ext.namespace(’Ext.ux.grid’);
BufferedGridView…. (line 19)
Ext is not defined
[Break on this error] Ext.namespace(’Ext.ux.data’);
BufferedJsonReade… (line 11)
Ext is not defined
[Break on this error] Ext.namespace(’Ext.ux.grid’);
BufferedRowSelect… (line 11)
Ext is not defined
[Break on this error] Ext.namespace(’Ext.ux.grid’);
BufferedStore.js (line 11)
Ext is not defined
[Break on this error] Ext.ux.ManagedIFrame = function(){
ext-ux-miframe.js (line 49)
Module XML:
Please help me, how to solve this problem
Thanks, khadaa
June 6th, 2008 at 11:35 pm
Looks like the ExtJS files are not being included properly. I have the library included in my project and Firebug doesn’t report any errors.
I would encourage you to use the Gwt-Ext forum. You will get a faster response there.
http://www.gwt-ext.com/forum
June 7th, 2008 at 5:23 am
Thanks a lot.
June 11th, 2008 at 10:37 pm
[...] CustomReader [...]
June 13th, 2008 at 5:30 pm
Good post …. but I have a better approach up my sleeve …. I can share if anyone is interested ?
June 13th, 2008 at 10:28 pm
I am!
August 7th, 2008 at 4:53 pm
really nice post. its been a while now that im planning to study Google Web Toolkit. Thanks for the nice article.
August 8th, 2008 at 12:31 am
Glad you liked it! CustomReader is now a part of GWT-Ext and available in the UX project. Check out the online demo at http://gwt-ext.com/demo-ux/#customReaderSample
You can download GWT-Ext-UX from http://code.google.com/p/gwt-ext-ux.