More on invoking scripts in Alfresco


In my last blogpost on this topic I wrote about invoking rhino scripts programatically from Alfresco and about passing its model into freemarker templates. After that I realized that alfresco templateService is unable to render more complex models (nested classes and collections) if called this way (please note on Alfresco webscripts invoked standard way this works correctly!). I think the problem is – no wrapper is used while passing model. I spent lot of time trying many things, read a lot of posts and topics on that, played with built-ins, trying to find that part in Alfresco source, but still no result, just exceptions. But solution is VERY SIMPLE – to write own templateService :-). Well, maybe this post is more about Freemarker and Rhino than Alfresco, sorry about that!

So, what I want to achieve? Suppose some simple script like this one:

[code lang=“javascript“]
model.bar="foo";
model.foo = "bar";
model.obj = {q:1, w:3, e:9};

model.arr = new Array();
model.arr[0] = {a:1, b:8, c:9, d:{d:66}};
model.arr[1] = {a:2, b:7, c:10, d:{d:66}};
model.arr[2] = {a:3, b:6, c:11, d:{d:66}};
model.arr[3] = {a:4, b:5, c:12, d:{d:66}};
[/code]

and a Freemarker template simple as that:

[code lang=“plain“]
q attribute: ${obj.q}

<#list arr?keys as value>
– ${value} = ${arr[value].a} = ${arr[value].b} = ${arr[value].c}
<#assign obj_d = arr[value].d>
+ d: ${obj_d.d}
</#list>
[/code]

Now after calling templateService.processTemplate( . . . ) the output is Exception this and Exception that… That happens, because Rhino creates NativeObjects for those objects and after passing those objects to Freemarker (through Java – without any ObjectWrapper), which doesn’t know how to handle them. The same situation happens when passing a collection with generics like <String, Object> into model – those Objects are standard Java objects and in Freemarker acts like them, they really don’t have attributes like q, a, b, etc. :-). Similar situation can happen with collections – they needs to be wrapped too. But stop with theory, back to practise. For those interested in wrapping and so, follow Freemarker documentation on wrappers.

Before wrapping – when I printed out keys of model, there were’nt my attributes defined in Javascript. What I found inside is on following listing, note my attributes are also there – bar, foo, arr, obj:

[code lang=“plain“]
<#assign keys = model?keys>
<#list keys as key>
+ ${key}
</#list>

(** output **)
+ getClass
+ clone
+ put
+ remove
+ get
+ equals
+ entrySet
+ class
+ hashCode
+ foo
+ keySet
+ size
+ clear
+ isEmpty
+ containsKey
+ values
+ arr
+ containsValue
+ empty
+ toString
+ putAll
+ bar
+ obj
[/code]

Those properties belongs to hashMap and is possible to get rid of them using simpleMapWrapper, this technique is described well in Rizwan Ahmed post.

What about my templateService implementation? First I tried to use JSONtoFmModel and convert javascript objects to strings, but that was no way forward. That was before I discovered wrappers :-). There is RhinoWrapper class in Freemarker and it solves all my problems! It can be used as simple as this:

[code lang=“java“]
Configuration cfg = new Configuration();
RhinoWrapper rhinoWrapper = new RhinoWrapper();

rhinoWrapper.setSimpleMapWrapper(true);
cfg.setObjectWrapper(rhinoWrapper);
[/code]

Previous listing creates new Freemarker configuration instance and rhino wrapper instance and binds them together. With that configuration it’s possible to create template instance, which takes reader with template content, let’s read it from repository:

[code lang=“java“]
NodeRef nodeRef = new NodeRef("workspace://SpacesStore/56ba2237-f776-494a-939b-d259b68c021a");

ContentReader contentReader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
InputStream inputStream = contentReader.getContentInputStream();

Template template = new Template("my_template", new InputStreamReader(inputStream), cfg);
[/code]

That’s all we need to render. Rendering takes one more parameter – model map, which is HashMap defined in my previous blogpost. To keep it simple (and post a little shorter) I’m writing output to sysout:

[code lang=“java“]
OutputStreamWriter streamWriter = new OutputStreamWriter(java.lang.System.out);
template.process(rootMap, streamWriter);
[/code]

Those few lines and everything works fine now! But I got some silver hairs with trying to solve that, now I can feel like a boss, so I achieved new „Alfresco guru badge“ :-). Also I’m thinking about some public GIT repository with my Alfresco inventions, what you think about it?

Like an Alfresco guru!
Like an Alfresco guru!

PostScriptum: Can anyone explain me why Alfresco uses Rhino version 1.6R7 which is from year 2007? :-O


Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *