Suggestion about the best BeansWrapper
Jacopo Cappellato <[email protected]>
| Newsgroups | gmane.comp.web.freemarker.user |
|---|---|
| Message-ID | <[email protected]> |
Hi all,
recently Daniel Dekany helped me in this list to fix a deadlock condition happening under special conditions when OFBiz (http://ofbiz.apache.org) screens were rendering Freemarker templates; during that interesting thread Daniel also mentioned that the way about the way we use BeansWrapper in OFBiz; here is the relevant part of the conversation:
=====================================================
QUESTION (me): Currently in OFBiz we are using the default object wrapper:
BeansWrapper defaultOfbizWrapper = BeansWrapper.getDefaultInstance();
do you think we should use a different one?
ANSWER (Daniel): I'm not sure. There are two problems with the current one:
- If you look at what map keys FreeMarker sees (`<#list k as
someMap?keys>${k} </#list>`), it mixes the actual Map keys with the
methods of the Map class. This I already find annoying enough to
stay away. But it's also dangerous, because if you are unlucky and
have key like "get", then someMap.get and someMap[myDynamicKey]
where myDynamicKey happens to be "get" will return the get method
instead of the map item. You can think further what if you have
a custom Map class and you add methods to it. Using
someMap.get("theKey") is robust however, but too verbose.
- It doesn't wrap W3C DOM nodes automatically in a way so that the
user can use FreeMarker's XML processing facility. It also doesn't
wrap Jython or Rhino JavaScript objects nicely.
What I like to use is:
BeansWrapper wrapper = new BeansWrapper();
wrapper.setSimpleMapWrapper(true);
This will not expose the methods of Map-s, only the keys of it. If you
want to get a map item that has a non-string key, you don't have
someMap.get(key) anymore, but you can use someMap(key) instead. If
it's a custom Map class with custom methods that you want to call from
the template, then you are out of luck, however. And in your case
these changes wouldn't be backward-compatible.
As of supporting W3C DOM nodes, Jython and such, it's very easy to
create a custom ObjectWrapper that does that. Just look at
DefaultObjectWrapper.
As of DefaultObjectWrapper, it's burdened with backward-compatibly,
and so wraps collections on an technically awkward way, plus it
doesn't provide any hacks for the non-String key problem. I don't see
a point of using that.
Anyway, the situation with Map-s and the "advertised" object wrappers
is something that had to be hammered out for ages. Sadly nobody has
found the time/incentive for it so far...
=====================================================
Based on Daniel's suggestions I have tried to replace:
BeansWrapper defaultOfbizWrapper = BeansWrapper.getDefaultInstance();
with
BeansWrapper defaultOfbizWrapper = new BeansWrapper();
defaultOfbizWrapper.setSimpleMapWrapper(true);
Unfortunately this change causes some errors in several screens because of ftl code snippets like:
1) ${currency.get("description",locale)}
2) <#assign roleType = quoteRole.getRelatedOne("RoleType")>
In the above examples "currency" and "quoteRole" are instances of GenericValue; this is a custom OFBiz class that implements Map<String, Object>
In order to fix these issues I have removed the method call:
defaultOfbizWrapper.setSimpleMapWrapper(true);
In this way the screens seem to work fine.
However I am not sure if there are side effects or if there are real advantages in using:
new BeansWrapper();
rather than:
BeansWrapper.getDefaultInstance();
Could you please provide some hints?
Thank you
Jacopo
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/