Calling the Render tags from Groovy Templates

The WebCenter Sites API does not have a full Java API for the Render tags. Creating a URL for a page or a blob in a Groovy element is not straight forward. Also calling another Template from a Groovy element or any java code, requires a bridge API. In this article we’ll explorer that API.

RunTag API

Since the earliest versions of WebCenter Site there has been support for XML elements and a corresponding XML Tag API. From XML Tags it was possible to call the WCS Java API. Also making calls to functionality embedded in the XML API from Java classes has been possible. The bridge between the Java API and the XML Tags has been a method called runTag() that is exposed through the ICS interface. The XML to Java bridge was implemented in a XML Tag: CALLJAVA. Since the support for JSP and Groovy elements this CALLJAVA tags is hardly used anymore.

The runTag() method is still used a lot as it provides access to functionality not exposed directly through a Java API. Although powerfull, the runTag() API does require some explanation as it has some easy to oversee quirks.

The contract for runTag() is simple; it requires a tagname and a list of arguments. It returns a string that must be streamed to the browser if it is not null.

Let me give an example: render.getbloburl. This XML Tag is very similar to it’s JSP equivalent, it renders a link to a blob. In the code sample below you can see how this Tag is called from a Groovy element.

If you check the documentation for the RENDER.GETBLOBURL XML Tag, you can see that the tags requires some arguments, some arguments are optional, others are optional. In the example below we must provide an output argument name (OUTSTR) and four arguments for the actual BlobServer logic. All of the argument names must be in capitals. This is the first quirk.

The second mistake that developers often make is that do not use the correct casing for the tagname. Some tags need to be called with a uppercase name, some with lowercase and for some it doesn’t matter. It is not well documented which tag requires which casing. In most cases is uppercase a safe bet.

Once the runTag() method is called the return values need to be handled. At first must the errno be checked for non-zero values. If errno was cleared before the runTag was invoked and after the call it has a negative value, the XML Tag has reported an error coding. Please check the WCS Developers guide for the possible errnos and how to correctly act upon. Llke in the example will it be correct in most cases to throw an exception.

If errno is zero the next step in the evaluation of the results need to happen: check if the return value from the runTag() method is not null. Some XML Tags will return a String, other will return null. Even based on different provided arguments might the Tag return a null or non-null value. This is again not well documented. At almost all cases it is correct to stream a non-null return value to the browser.

The last evaluation is the return value of the XML Tag itself. The getbloburl tag will add a variable on the ICS variable scope with the name provided in the OUTSTR argument. In our case we just stream this to the browser as text. In real life it is more likely that this return value is used as data for a HTML <img> or <href> tag.

import COM.FutureTense.Interfaces.FTValList
import COM.FutureTense.Interfaces.ICS

def blobUrl(ICS ics,long id){
	//all capitals
	FTValList list = new FTValList();

	list.put("OUTSTR" ,"htmlurl");

	list.put("BLOBTABLE" ,"MungoBlobs");
	list.put("BLOBKEY", "id");
	list.put("BLOBCOL", "urldata");
	list.put("BLOBWHERE",Long.toString(id));

	if (ics.GetErrno() != 0) {
		ics.ClearErrno();
	}

	String tagName = "RENDER.GETBLOBURL";

	String s = ics.runTag(tagName, list);

	if (ics.GetErrno() != 0) {
		throw new RuntimeException("ics.runTag(" + tagName + ") returned errno: " +  ics.GetErrno() + ". The input arguments are: "+ list + ". ComplexError is "+
		ics.getComplexError().getMessage()+ ", pagename: "+ ics.GetVar("pagename") + ", elementname: "+ ics.ResolveVariables("CS.elementname"));
	}
	if (s !=null){
		ics.StreamEvalBytes(s);
	}
	String url = ics.GetVar("htmlurl");
	ics.RemoveVar("htmlurl");
	return url;

}

ics.StreamEvalBytes(blobUrl(ics,1374098073866L));

 

CallTemplate

A slightly more complex example is the render.calltemplate tag. There is also no equivalent Java API to inject a Template into the current rendering stream so we have to rely on runTag() again.

 

import COM.FutureTense.Interfaces.FTValList
import COM.FutureTense.Interfaces.ICS

def callTemplate(ICS ics,String c,long id){

	FTValList list = new FTValList();

	list.put("SITE","avisports");
	list.put("SLOTNAME","mySlot");
	list.put("TID",ics.GetVar("eid"));

	list.put("TTYPE","CSElement");
	list.put("C",c);
	list.put("CID",Long.toString(id));
	list.put("TNAME","HomeLayout");
	list.put("CONTEXT","");
	list.put("STYLE","element");
	if (ics.GetErrno() != 0) {
		ics.ClearErrno();
	}

	String tagName = "RENDER.CALLTEMPLATE";

	String s = ics.runTag(tagName, list);

	if (ics.GetErrno() != 0) {
		throw new RuntimeException("ics.runTag(" + tagName + ") returned errno: " +  ics.GetErrno() + ". The input arguments are: "+ list + ". ComplexError is "+
		ics.getComplexError().getMessage()+ ", pagename: "+ ics.GetVar("pagename") + ", elementname: "+ ics.ResolveVariables("CS.elementname"));
	}
	if (s !=null){
		ics.StreamEvalBytes(s);
	}

}

callTemplate(ics,"Page",1346134315032L);

As you can see is the construct to call a Template very similar to generating a url for a blob, a set of arguments passed to the tag and the result is to be streamed to the browser. In the cases where STYLE=element or STYLE=embedded, will the return value of runTag() be null. The XML tag will include the response into the output stream itself. This is because under the covers the STYLE=element variant is invoking ics.CallElement and STYLE=embedded is executing ics.InsertPage. Both these calls have no return value.

CallTemplate with arguments

The last quirk is would like to discuss the variant where non-standard arguments need to be passed to the calltemplate tag. If you want to add additional arguments to a XML or JSP calltemplate tag you would nest one or more <render.argument> tags to the calltemplate tag. The JSP or XML templating engine would add these arguments to the executing tag for you. Since the runTag() method does not allow for nesting of arguments and does not have an parameter that could accept nested arguments a different construct must be used. The contact is to prefix the additional arguments with ‘ARGS_’. If for example you want to pass besides the standard arguments like ‘c’ and ‘cid’, also a ‘color’ argument you would need to put (“ARGS_color”, “green”) to the FTValList.

def callTemplateWithArgs(ICS ics,String c,long id, Map args){

	FTValList list = new FTValList();

	list.put("SITE","avisports");
	list.put("SLOTNAME","mySlot");
	list.put("TID",ics.GetVar("eid"));

	list.put("TTYPE","CSElement");
	list.put("C",c);
	list.put("CID",Long.toString(id));
	list.put("TNAME","SectionLayout");
	list.put("CONTEXT","");
	list.put("STYLE","element");

	args.each {entry ->
		list.put("ARGS_" + entry.key, entry.value);
	}

	if (ics.GetErrno() != 0) {
		ics.ClearErrno();
	}

	String tagName = "RENDER.CALLTEMPLATE";

	String s = ics.runTag(tagName, list);

	if (ics.GetErrno() != 0) {
		throw new RuntimeException("ics.runTag(" + tagName + ") returned errno: " +  ics.GetErrno() + ". The input arguments are: "+ list + ". ComplexError is "+
		ics.getComplexError().getMessage()+ ", pagename: "+ ics.GetVar("pagename") + ", elementname: "+ ics.ResolveVariables("CS.elementname"));
	}
	if (s !=null){
		ics.StreamEvalBytes(s);
	}

}

callTemplateWithArgs(ics,"Page",1346134315808L, ['bg-color':"green",'site': 'avisports']);

 

You may wonder why in this example the ‘site’ argument is also added to the additional arguments, as it was already included as a normal SITE argument. It turns out that for the variant STYLE=element, the SITE argument is not added to the arguments that is used to call the Template. This leads to the behaviour that if you use a wrapper element that is used by many sites, the incorrect site argument might be passed to the called Template, leading to a site that renders incorrect.

 

I hope you get the basic idea on how to use the runTag() construct. It allows a path for a nice set of facades around the XML tags to be used from Groovy and Java.

Comments

  1. Freddy Villalba Arias says:

    Is there an equivalent to ics.runTag() for invoking JSP tags from POJOs (assuming we can access a legitimate, “real” ICS object from within the POJO)?

Add Your Comment