PDA

View Full Version : BeanModel generation with AutoBean support



stigrv
26 Jan 2011, 2:06 AM
We've recently converted our in development webapplication from using GWT-RPC to using RequestFactory. In this process we could not get the BeanModelGenerator to read the getters from the proxy interface. The following generator fixes this problem.



package com.wis.wisweb2.intra.core.rebind;

import java.util.List;

import com.extjs.gxt.ui.rebind.core.BeanModelGenerator;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.requestfactory.shared.BaseProxy;

/**
* BeanModel generator with support for GWT AutoBeans.
*
* @author Stig Runar Vangen
*/
public class AutoBeanBeanModelGenerator extends BeanModelGenerator {

@Override
protected final void addGetters(final JClassType cls,
final List<JMethod> methods) {
// Ignore methods of Object
if (cls.getSuperclass() != null) {
addGetters(cls.getSuperclass(), methods);
addClassGetters(cls, methods);
}

if (isAutoBean(cls)) {
addClassGetters(cls, methods);
}
}

/**
* @param cls
* class to test
* @return <i>true</i> if given class qualify for AutoBean generation
*/
private boolean isAutoBean(final JClassType cls) {
if (cls.getQualifiedSourceName().equals(BaseProxy.class.getName())) {
return true;
}

for (JClassType classType : cls.getImplementedInterfaces()) {
return isAutoBean(classType);
}

return false;
}

private void addClassGetters(final JClassType cls,
final List<JMethod> methods) {
for (JMethod m : cls.getMethods()) {
if (m.isPublic() || m.isProtected()) {
String name = m.getName();
if ((name.matches("get.*") || name.matches("is.*"))
&& m.getParameters().length == 0) {
methods.add(m);
}
}
}
}
}


You will also need to define this generator for use in your GWT module definition file.



<!-- BeanModel for AutoBean -->
<generate-with class="com.wis.wisweb2.intra.core.rebind.AutoBeanBeanModelGenerator">
<when-type-assignable class="com.extjs.gxt.ui.client.data.BeanModelLookup" />
</generate-with>


This definition should override the generator from the ExtGWT package.

Stig Runar Vangen
Waade Information System

mxhn
28 Feb 2011, 7:25 AM
Hi,

Thank you for sharing this. I've been looking for a way to use ExtGWT with GWT 2.1.1 RequestFactory as well, but I couldn't find any infos about this on forums, etc. I'm glad I've found your post, but I have some questions:

Did you find easy to work with ExtGWT + RequestFactory after making this patch?
Did you encounter any other issues after doing this?
By defining the generate-with in GWT module def file, does this mean all your *Proxy client beans will be automatically wrapped by the generator?
Suppose you have a PersonProxy bean that has the name property. How do you specify in code that the AutoBean for this should be used for filling a TreePanel?


Thanks

stigrv
28 Feb 2011, 9:08 AM
Glad you liked the post.

> Did you find easy to work with ExtGWT + RequestFactory after making this patch?
We've been using this factory for over a month now, and I've never had any problems with it.

> Did you encounter any other issues after doing this?
This factory seems to work exactly like the one provided in GXT.

> By defining the generate-with in GWT module def file, does this mean all your *Proxy client beans will be automatically wrapped by the generator?
Using the generate-with replaces the GXT factory with the provided one. You will still have to tag your model classes in the same way as if you wanted them to be processable by the old factory.

> Suppose you have a PersonProxy bean that has the name property. How do you specify in code that the AutoBean for this should be used for filling a TreePanel?
You set the display property as you would do otherwise:
tree.setDisplayProperty("name");

mxhn
28 Feb 2011, 10:01 AM
I've tried to make one of my proxies, interface CityProxy extends ValueProxy, ModelData so that the generator will automatically generate a ModelData implementing wrapper for it. Not sure this is the way to use actually use the generator you built, but it throws this error:

[INFO] Invoking generator com.google.gwt.requestfactory.rebind.RequestFactoryGenerator
[INFO] [ERROR] The method public abstract <X extends java.lang.Object> X get(java.lang.String property) is neither a getter nor a setter
[INFO] [ERROR] Invalid Request parameterization java.util.Map
[INFO] [ERROR] Requests that return collections may be declared with java.util.List or java.util.Set only
[INFO] [ERROR] The method public abstract <X extends java.lang.Object> X remove(java.lang.String property) is neither a getter nor a setter
[INFO] [ERROR] The method public abstract <X extends java.lang.Object> X set(java.lang.String property, X value) is neither a getter nor a setter
[INFO] [ERROR] Unable to create RequestFactoryModel model due to previous errors

Also, if I try to make "City implements BeanModelTag" and remove that "CityProxy extends ModelData", the onSuccess method from my request, returns a CityProxy that doesn't extend ModelData.

Is there anything I could debug/test in order to fix this? If I did something wrong applying your solution, I'd be grateful if you could point me in the right direction.

Thanks!

stigrv
28 Feb 2011, 10:25 AM
Could you post the code for the Proxy and the Request? It might be easier for me to see what the problem consists of if I see the code.

mxhn
28 Feb 2011, 10:26 AM
Yes, just a few moments to extract it. Thank you!

mxhn
28 Feb 2011, 10:35 AM
This is the code: http://pastebin.com/cYuuB2Ce

mxhn
28 Feb 2011, 11:14 AM
All examples that I found seem to use RPC with BeanModelTag. I don't have a clear idea how to integrate RequestFactory with this. How did you make it work with RequestFactory?

Thank you!

stigrv
28 Feb 2011, 11:26 AM
You'll need to convert the proxy object to a BeanModel instance using the BeanModelFactory before adding it to the ModelData list. Read more on how to do this here:

http://www.sencha.com/helpcenter/index.jsp?topic=/com.extjs.gxt.help/html/tutorials/beanmodel.html

mxhn
28 Feb 2011, 11:29 AM
I'll try that, I appreciate your help with this!

mxhn
28 Feb 2011, 12:21 PM
It really works! I'm very happy. Thank you a lot! I hope you have great success in what you do! You helped me so much with this!

mxhn
1 Mar 2011, 4:05 AM
Today, I've tried a new thing that would make it much easier to map the server beans directly to GXT components like the TreePanel. I'm trying to use TreeLoader and TreeBeanModelReader to directly map the returned beans from server. This is the code:


TreeBeanModelReader tbmr = new TreeBeanModelReader();
BaseTreeLoader<ModelData> treeLoader = new BaseTreeLoader<ModelData>(tbmr);

TreeStore<ModelData> store = new TreeStore<ModelData>(treeLoader);
treePanel = new TreePanel(store);

//do a Request to return a list of CityProxy that implement BeanModelTag
req.getAllCities(cp, "extra_param_value").fire(
new Receiver<List<CityProxy>>() {

@Override
public void onSuccess(List<CityProxy> cities) {
treeLoader.load(data);
}

});

The issue is that the returned list cities doesn't contain instances of CityProxy but instances of CityProxyAutoBean_com_google_gwt_requestfactory_shared_impl_EntityProxyCategory_com_google_gwt_requestfactory_shared_impl_ValueProxyCategory_com_google_gwt_requestfactory_shared_impl_BaseProxyCategory extends com.google.gwt.autobean.shared.impl.AbstractAutoBean<com.tion.shared.CityProxy>.

All these instances are not implementing BeanModelTag. TreeBeanModelReader tries to do this:


BeanModelFactory factory = BeanModelLookup.get().getFactory(beans.get(0).getClass()); and it returns NULL because beans.get(0).getClass() doesn't implement BeanModelTag.


Is there any way to make this work?


Thank you!

mxhn
1 Mar 2011, 11:37 PM
Any ideas?

mxhn
7 Mar 2011, 5:22 AM
I think all this is going down the "reinvent the wheel" road because GXT 3.0 will include full RequestFactory support.

@Stigrv, you said you converted your development webapplication from using GWT-RPC to using RequestFactory but did you redesign all GXT's classes like(BaseTreeLoader, BaseLoader, TreeStore) which still use GWT-RPC implementations or you just have something that works partially and you're still waiting for GXT 3.0 to be released?

I want to know this because I feel like this is no more a trivial change in GXT's code but a full implementation that is going to be released by them. So maybe it would be better to use GWT-RPC and then to switch to RequestFactory when GXT 3.0 gets released.

What was your motivation to switch to RequestFactory so early? How much time did you invest in getting GXT to be compatible with RequestFactory?

Thank you!

stigrv
7 Mar 2011, 10:37 AM
Sorry I didn't see your message earlier.

For the proxy that fails to be converted using the BeanModelFactory, you'll need to tag the proxy with BeanModelTag in order for the factory to know about and be able to convert it.

Loaders and stores are untouched, and are wired to RF instead of GWT-RPC in the presenter/activity layer.

For my motivation in switching to RF this early, this was mainly as an attempt to prepare our application for GXT3, mainly to reduce the work needed when it is released. It has to be noted that the application we are making is still in its early phase, and it is therefore not guaranteed that we've found all problems yet. With that being said, the only change I've done is the custom implementation of the generator. This generator solved the problem with empty beans that occured after converting the application into using RF instead og GWT-RPC.

It should also be noted that it took me over a week to convert our current codebase from GWT-RPC into RF and the rest of the GWT Bindings framework. Now that we are implementing more of the views needed, this was well spent time, as it will save us the work of doing this later, and therefore reducing duplicate work.

mxhn
7 Mar 2011, 11:33 PM
Thank you for helping me understand your motivation. It makes sense! I also feel this is the best option at the moment instead of going back to RPC and then, in a few months time, having to upgrade again. But I must admit I also had the feeling of not "reinventing the wheel" and I thought there were many changes to get RequestFactory integrated. I'm glad you and Colin Alworth made me feel more confident about this and go for RequestFactory. I appreciate you guys helped me!

Regarding the changes needed, I also found the need to override the TreeBeanModelReader(I'm using TreePanel mostly at this moment). I think in case I'll add components like grids, I'd have to also override BeanModelReader as well.

This is because GXT tries to get the BeanModel factory by using BeanModelFactory factory = BeanModelLookup.get().getFactory(beans.get(0).getClass()); and this returns NULL. GWT's RequestFactory generator takes the proxy interfaces(that include the ones tagged with BeanModelTag) and generates an AutoBean implementation but which isn't tagged directly with BeanModelTag. This implementation is not found by TreeBeanModelReader which returns an assertion error for such beans. Please see the whole issue here (http://www.sencha.com/forum/showthread.php?125509-Mapping-GWT-RequestFactory-proxies-to-GXT-components).

If this is a change that you already thought about and have an alternative fix or perhaps if you missed it, I'd be glad to know your thoughts about it.

Algiano
23 Jul 2011, 4:25 PM
Hi guys,

I'm also trying to implement this solution and I'm getting the following error. Any ideas?



[ERROR] [] - Line 16: No source code is available for type com.extjs.gxt.ui.rebind.core.BeanModelGenerator; did you forget to inherit a required module?
[ERROR] [] - Line 19: No source code is available for type com.google.gwt.core.ext.typeinfo.JClassType; did you forget to inherit a required module?
[ERROR] [] - Line 19: No source code is available for type com.google.gwt.core.ext.typeinfo.JMethod; did you forget to inherit a required module?


com.google.web.bindery.event.shared.UmbrellaException: One or more exceptions caught, see full set in UmbrellaException#getCauses
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext$StandardPayloadDialect.processPayload(AbstractRequestContext.java:299)
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext$4.onTransportSuccess(AbstractRequestContext.java:951)
at com.google.web.bindery.requestfactory.gwt.client.DefaultRequestTransport$1.onResponseReceived(DefaultRequestTransport.java:136)
at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:287)
at com.google.gwt.http.client.RequestBuilder$1.onReadyStateChange(RequestBuilder.java:395)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:167)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessagesWhileWaitingForReturn(BrowserChannelServer.java:326)
at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:207)
at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:132)
at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:561)
at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:269)
at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:214)
at sun.reflect.GeneratedMethodAccessor25.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:167)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:281)
at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:531)
at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:352)
at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.AssertionError: No BeanModelFactory found for class com.ronin.kioos.mapdesigner.client.connectivity.proxy.MallProxyAutoBean_com_google_web_bindery_requestfactory_shared_impl_EntityProxyCategory_com_google_web_bindery_requestfactory_shared_impl_ValueProxyCategory_com_google_web_bindery_requestfactory_shared_impl_BaseProxyCategory$1
at com.extjs.gxt.ui.client.data.BeanModelReader.read(BeanModelReader.java:48)
at com.extjs.gxt.ui.client.data.BeanModelReader.read(BeanModelReader.java:1)
at com.extjs.gxt.ui.client.data.MemoryProxy.load(MemoryProxy.java:48)
at com.extjs.gxt.ui.client.data.BaseLoader.loadData(BaseLoader.java:134)
at com.extjs.gxt.ui.client.data.BaseLoader.load(BaseLoader.java:98)
at com.extjs.gxt.ui.client.data.BaseLoader.load(BaseLoader.java:92)
at com.ronin.kioos.mapdesigner.client.views.MapCanvas$1.onSuccess(MapCanvas.java:201)
at com.ronin.kioos.mapdesigner.client.views.MapCanvas$1.onSuccess(MapCanvas.java:1)
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequest.onSuccess(AbstractRequest.java:123)
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext$StandardPayloadDialect.processPayload(AbstractRequestContext.java:266)
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext$4.onTransportSuccess(AbstractRequestContext.java:951)
at com.google.web.bindery.requestfactory.gwt.client.DefaultRequestTransport$1.onResponseReceived(DefaultRequestTransport.java:136)
at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:287)
at com.google.gwt.http.client.RequestBuilder$1.onReadyStateChange(RequestBuilder.java:395)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:167)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessagesWhileWaitingForReturn(BrowserChannelServer.java:326)
at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:207)
at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:132)
at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:561)
at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:269)
at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:214)
at sun.reflect.GeneratedMethodAccessor25.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:167)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:281)
at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:531)
at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:352)
at java.lang.Thread.run(Thread.java:619)


Thanks,
Ale

Twigger
12 Oct 2011, 5:15 AM
This is the code: http://pastebin.com/cYuuB2Ce
Thank you so much. It helped me to resolve my problem. Code is fine!

abshnasko
15 Nov 2011, 11:39 AM
That pastebin code is no longer there. I am having the same problem as Algiano. What is the solution?