PDA

View Full Version : GXT BeanModelTag and model creation issue



huntc
3 Mar 2011, 2:16 AM
Hi there,

I'm presently having an issue with GXT around its BeanModel architecture.

To associate a list of objects with a store I'm using a command like the following:



recentTradesStore.add(BeanModelLookup.get()
.getFactory(SimpleDealMasterDTO.class).createModel(trades));


SimpleDealMasterDTO is my data transfer class and trades is a list of them i.e. List< SimpleDealMasterDTO>.

SimpleDealMasterDTO is declared like this:


public class SimpleDealMasterDTO extends JavaScriptObject implements
BeanModelTag {

So far so good. However if I introduce another DTO in the same package e.g.:


public class DealMasterCommonDTO extends JavaScriptObject implements
BeanModelTag {

...then when I create the model against the store, proxies around DealMasterCommonDTO are created instead of SimpleDealMasterDTO objects.

I'm suspecting that there could be an issue with GXT's BeanModelFactory class in that it wrongly creates instances where they share the same super class (just a guess).

Any help and guidance would be most appreciated.

Kind regards,
Christopher

Colin Alworth
3 Mar 2011, 2:29 PM
Unless I am misunderstanding, the issue is with you requesting a factory that works with SimpleDealMasterDTO, and expecting it to automatically work with DealMasterCommonDTO. Most of the BeanModelReaders are designed in such a way as to allow inspecting each model to see if it needs a different factory - I suggest you look into those.

I would also suggest considering a different way of solving this problem, especially if you must use javascript overlay types. Those types are already capable of being treated like a map from String to Object. Consider making a superclass of your existing DTOs which implements ModelData and extends JavaScriptObject, and avoiding the bean model factory altogether - this will save you some space and processing time in dealing with your dtos.

huntc
3 Mar 2011, 2:45 PM
Unless I am misunderstanding, the issue is with you requesting a factory that works with SimpleDealMasterDTO, and expecting it to automatically work with DealMasterCommonDTO. Most of the BeanModelReaders are designed in such a way as to allow inspecting each model to see if it needs a different factory - I suggest you look into those.

Thanks for the reply. What I'm stating is that the factory creates SimpleDealMasterDTO unless DealMasterCommonDTO is declared as implementing BeanModelTag. When I do declare DealMasterCommonDTO as implementing BeanModelTag, the factory for SimpleDealMasterDTO fails to return wrappers around SimpleDealMasterDTO. Instead, the factory for SimpleDealMasterDTO returns wrappers around DealMasterCommonDTO!


I would also suggest considering a different way of solving this problem, especially if you must use javascript overlay types. Those types are already capable of being treated like a map from String to Object. Consider making a superclass of your existing DTOs which implements ModelData and extends JavaScriptObject, and avoiding the bean model factory altogether - this will save you some space and processing time in dealing with your dtos.
Thanks I will look at implementing ModelData directly.

Colin Alworth
3 Mar 2011, 2:50 PM
Any type you wish to convert in a bean model factory must implement BeanModelTag - it is not sufficient to implement it once at a high level and call it good. Additionally, as I said before, you must get the correct instance of the factory for each object you wish to convert.

I still don't think I am understanding your problem - can you provide a bit of sample code which outlines the issue you are having, provided these it meets the two statements I made above?

huntc
3 Mar 2011, 3:04 PM
Hi Colin,

I really appreciate the replies; sorry if I'm not making myself clear. The two DTO classes both implement BeanModelTag. However the factory for SimpleDealMasterDTO returns DealMasterCommonDTO beans. If I subsequently remove the "implements BeanModelTag" for DealMasterCommonDTO then the factory for SimpleDealMasterDTO starts returning SimpleDealMasterDTO beans.

I'm not sure I can provide any more meaningful code to what I initially provided. 'hope that you can help still.

Kind regards,
Christopher

Colin Alworth
3 Mar 2011, 3:20 PM
Simply having two classes in the same package should not affect this.


public class SimpleDealMasterDTO extends JavaScriptObject implements BeanModelTag {...}
public class DealMasterCommonDTO extends JavaScriptObject implements BeanModelTag {...}

BeanModel getBean(JavaScriptObject randomObject) {
return BeanModelLookup.get().getFactory(randomObject.getClass()).createModel(randomObject);
}
My understanding is that this should work correctly, and if it doesn't, more info will be needed about your setup, or the generated code that is built at compile time. Use the -gen option for the compiler, or look in target/.generated if you use maven. But really, this should work – please post more of your usecase, preferably in EntryPoint form, that demonstrates the issue.

huntc
3 Mar 2011, 3:48 PM
Just to correct your example:

public class SimpleDealMasterDTO extends JavaScriptObject implements BeanModelTag {...}
public class DealMasterCommonDTO extends JavaScriptObject implements BeanModelTag {...}

BeanModel getBean(SimpleDealMasterDTO simpleDealMasterDTO) {
return BeanModelLookup.get().getFactory(SimpleDealMasterDTO.class).createModel(simpleDealMasterDTO);
}

SimpleDealMasterDTO objects should always be returned. What I'm seeing is that a DealMasterCommonDTO object is being return with the above code.

Incidentally I'm using .class (not .getClass()). That wouldn't make a difference would it?

Colin Alworth
3 Mar 2011, 3:50 PM
Yes – that is what i was saying about getting a factory for the right type.

Either call it with instance.getClass(), or know ahead of time which factory you want from the lookup, and pass in that literal. But dont expect it to work with other types.

huntc
3 Mar 2011, 4:00 PM
OK, I'm confused. In Java both the classname.class and the instancename.getClass should be returning the same Java Class object. Why would it make a difference then if I use classname.class instead of instancename.getClass?

Colin Alworth
3 Mar 2011, 4:20 PM
MyClass.class returns the class that maps to MyClass, whereas MyClass instance = ...; instance.getClass() returns the exact class of that instance, which may be a subclass. In some cases, you want the former, in others, the latter. If you want a factory that can deal with MyClassA and _only_ with MyClassA, you can say BeanModelLookup.get().getFactory(MyClassA.class), or you can say (where a is an instance of MyClassA) BeanModelLookup.get().getFactory(a.getClass()). In this case, where a will always be exactly of type MyClassA, the former is probably better.

If you expect to be passing in more than one type and want it turned into a bean model, you must have the correct factory for that type. Otherwise, you'll get back a factory designed to deal with a different type entirely, and it cant handle it - this is what is causing your issue.

If you say BeanModelLookup.get().getFactory(MyClassB.class), and then use that factory on an instance of MyClassA, there will be issues*. This is what it is appears you are doing. This is the case because each class tagged with ModelBean gets its own factory made just for it, and if you try to share, problems will occur. Perhaps some generics could make this easier to understand, but thats a topic for another day.

In short, there is a difference, but only if you expect to be needing factories for more than one type, which you do. That said, if you can always call getFactory on the exact type that will be passed in to createModel, then all is well - this may be possible for you, depending on how you have your code. But since you have JS overlay types, which can be freely cast** from one type to another, you might confuse the factory even if you do say .getClass()... Which brings up my first posts point about just making a ModelData wrapper.


*Almost always - there are, of course, exceptions, but none that are important to this discussion.
**If you know Java very well, and only a little of GWT or JS, this might surprise you, but it is a necessary feature of extending JavaScriptObject:
SimpleDealMasterDTO simpleDeal = ...;
DealMasterCommonDTO dealMaster = simpleDeal.cast();

huntc
3 Mar 2011, 4:36 PM
Hi Colin, once again I really appreciate this dialog.


MyClass.class returns the class that maps to MyClass, whereas MyClass instance = ...; instance.getClass() returns the exact class of that instance, which may be a subclass. In some cases, you want the former, in others, the latter. If you want a factory that can deal with MyClassA and _only_ with MyClassA, you can say BeanModelLookup.get().getFactory(MyClassA.class), or you can say (where a is an instance of MyClassA) BeanModelLookup.get().getFactory(a.getClass()). In this case, where a will always be exactly of type MyClassA, the former is probably better.
That's what I'm doing though! I want a SimpleDealMasterDTO bean and nothing else in this case.


If you expect to be passing in more than one type and want it turned into a bean model, you must have the correct factory for that type. Otherwise, you'll get back a factory designed to deal with a different type entirely, and it cant handle it - this is what is causing your issue.
I'm only passing in one type.


If you say BeanModelLookup.get().getFactory(MyClassB.class), and then use that factory on an instance of MyClassA, there will be issues*. This is what it is appears you are doing.
No it isn't!


This is the case because each class tagged with ModelBean gets its own factory made just for it, and if you try to share, problems will occur. Perhaps some generics could make this easier to understand, but thats a topic for another day.
I'm not trying to share a factory.


In short, there is a difference, but only if you expect to be needing factories for more than one type, which you do.
No I don't!


That said, if you can always call getFactory on the exact type that will be passed in to createModel, then all is well - this may be possible for you, depending on how you have your code. But since you have JS overlay types, which can be freely cast** from one type to another, you might confuse the factory even if you do say .getClass()... Which brings up my first posts point about just making a ModelData wrapper.
I'm now implementing the ModelData interface. I think that this leads to better code anyhow.

I shall post my results of using ModelData with JavaScriptObject subclasses.

Thanks!

Colin Alworth
3 Mar 2011, 4:45 PM
Then it is very likely that beanmodel is having issues with js overlay types. I'll try it out when i've got a free moment.

One more option for you, this being the case - wrap your data in a real java type. That is, wrap the js object, with its funny casting features, in a java type so that they cant affect other systems.