cancel
Showing results for 
Search instead for 
Did you mean: 

Adding custom Java Objects in Freemarker Model

cedric_lamalle
Champ on-the-rise
Champ on-the-rise

Hello,

I'm creating a custom form control and to render it I need to add custom java objects in the FreeMarker Model. From the documentation at Template models | Alfresco Documentation  it seems to be possible to add this objects from a Spring Configuration. I've tried to search through Alfresco Source Code, and found some example of how the default model is added to Freemarker in repository/config/alfresco/template-services-context.xml.

So in my repo project I added:

<bean id="jsonToModelTemplateExtension" parent="baseTemplateImplementation" class="x.x.x.x.JsonTemplateExtension">

   <property name="extensionName">

   <value>jsonToModel</value>

   </property>

</bean>

The java code is:

public class JsonTemplateExtension extends BaseTemplateProcessorExtension implements TemplateMethodModelEx {

   private static final Log LOG = LogFactory.getLog(JsonTemplateExtension.class);

   @Override
   public Object exec(List arguments) throws TemplateModelException {

   LOG.warn(arguments);

   return "Hello World!";

  }

}

Then in my share project I've tried to use the Template Model Method like this:

<#assign mytext = jsonToModel('Teste')>
${mytext}<br/>

And I'm getting this error:

Caused by: freemarker.core.InvalidReferenceException: The following has evaluated to null or missing:

==> jsonToModel  [in template "org/alfresco/components/form/controls/conteudoSolicitacao.ftl" at line 4, column 26]

Any idea of what I am doing wrong?

Thanks,

Cedric.

1 ACCEPTED ANSWER

jpotts
World-Class Innovator
World-Class Innovator

I've taken your Spring bean, your Java class, and your freemarker snippet and ran them successfully unchanged, all within the repo tier.

It looks like you may be defining your bean in the repo tier, but then you are trying to run freemarker in the Share tier. Those are two completely different web applications, so if you want to use your Freemarker extension in the Share tier, you need to put your context file in the Share web app instead of the repo.

View answer in original post

5 REPLIES 5

jpotts
World-Class Innovator
World-Class Innovator

Where did you put your Spring context file and your compiled class?

Were there any errors reported in the log other than the InvalidReferenceException?

Hi,

The Spring context file is in classpath:alfresco/module/${project.artifactId}/context/service-context.xml and the compiled class had been packed in a jar in the generated amp file.

I've put a breakpoint in org.alfresco.repo.processor.BaseProcessorExtension#register() and saw that the TemplateExtension I created was registered against the FreeMarkerProcessor as are the standard ones (xmldate for instance).

public void register()

{

  this.processor.registerProcessorExtension(this);

}

Thanks,

Cedric.

Hi,

I'm attaching the log from the start of Tomcat until the error and the structure of the generated AMP.

jpotts
World-Class Innovator
World-Class Innovator

I've taken your Spring bean, your Java class, and your freemarker snippet and ran them successfully unchanged, all within the repo tier.

It looks like you may be defining your bean in the repo tier, but then you are trying to run freemarker in the Share tier. Those are two completely different web applications, so if you want to use your Freemarker extension in the Share tier, you need to put your context file in the Share web app instead of the repo.

Hi Jeff,

DOH!

You're right. They really are two different beasts!

In Share the only way I've found until now to customize the template is to override webscripts.container:

<bean id="webscripts.container" parent="webscripts.abstractcontainer" class="org.springframework.extensions.webscripts.LocalWebScriptRuntimeContainer">

   <property name="name"><value>Surf Container</value></property>

   <property name="registry" ref="webscripts.registry" />

   <property name="searchPath" ref="webframework.webscripts.searchpath" />

   <property name="templateProcessorRegistry" ref="webframework.webscripts.registry.templateprocessor" />

   <property name="scriptProcessorRegistry" ref="webframework.webscripts.registry.scriptprocessor" />

   <property name="scriptParameterFactoryRegistry" ref="webscripts.web.scriptparameterfactoryregistry" />

   <property name="configService" ref="web.config" />

   <property name="scriptObjects">

      <map merge="true">

         <entry key="remote" value-ref="webframework.webscripts.scriptremote" />

      </map>

   </property>

   <property name="processorModelHelper" ref="processor.model.helper"/>

   <property name="extensibilityModuleHandler" ref="webscripts.extensibility.handler"/>

   <property name="urlModelFactory" ref="url.model.factory"/>

   <!-- Override default Template Model -->

   <property name="templateObjects">

      <map merge="true">

         <entry key="jsonToModel">

            <bean class="br.gov.mpog.pocpdc.JsonTemplateExtension" />

         </entry>

      </map>

   </property>

</bean>

I've seen that Spring Surf overrides the webscripts.container in spring-surf/src/main/resources/org/springframework/extensions/surf/spring-surf-application-context.xml that is already defined in Spring WebScripts spring-webscripts/src/main/resources/org/springframework/extensions/webscripts/spring-webscripts-application-context.xml, so this is why I'm using LocalWebScriptRuntimeContainer to override webscripts.container.

Thanks for your time!