Jun 04

Gwt-Ext Custom Reader

Tag: Ext, Gwt, Java, Javascript, Programming, WebAbhijeet Maharana @ 11:36 pm

Screenshot

If I had to populate a Gwt-Ext store with data in arbitrary format,

  1. I could parse data on the client and use a MemoryProxy + ArrayReader or

  2. 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

8 Responses to “Gwt-Ext Custom Reader”

  1. khadaa says:

    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

  2. Abhijeet Maharana says:

    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

  3. khadaa says:

    Thanks a lot.

  4. Gwt-Ext 2.0.4 is out | Abhijeet Maharana says:

    [...] CustomReader [...]

  5. Free Url Proxy says:

    Good post …. but I have a better approach up my sleeve …. I can share if anyone is interested ?

  6. Abhijeet Maharana says:

    I am!

  7. insic says:

    really nice post. its been a while now that im planning to study Google Web Toolkit. Thanks for the nice article.

  8. Abhijeet Maharana says:

    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.

Leave a Reply