Aggregating WCS Content as a Service

Often it is necessary to exchange aggregated content from one system to another. By “aggregated” I mean that the complexity of the underlying Model is partially hidden from the caller and that the system doing the serving is free to make model changes without having to notify the caller. Additionally, the system providing the service will generally “clean up” and denormalize the data as appropriate for the service. As such, the response might include various bits of content from many assets (known only by the system providing the service), not just the one asset being requested. Being a mature and flexible CMS system, Webcenter Sites is ideally suited to provide such an abstraction when integrating with other applications.

Let’s take for a trivial example a simple flex Article asset that uses a CKEdit attribute as a container for its bodytext. And let’s say we want WCS to do nothing more than provide the plain-jane content for this Article to another system. OOTB, we could export publish the content as pure XML, but the problem is that if we took this approach, then the content exported for the CKEdit attribute would not be able to include the values of any embed assets’ data within the exported content because, when exported as XML, such data is not evaluated and only the embedded assetid references are provided. The following screenshot shows an all-to-typical use of the CKEdit attribute: to embed another asset inside the body field (in this case an image asset, but of course it could be anything):

Screen Shot 2014-06-09 at 1.29.36 PM

And below is an example of the above content exported as XML using XML Export. Note the (basically unusable) embedded asset reference inside the body.

<?xml version="1.0"?>
<document>
<asset id="1330881053255" type="AVIArticle" subtype="Article">
<attribute name="Attribute_headline"><string value="Agility Drills"/></attribute>
<attribute name="flextemplateid"><assetreference type="ContentDef" value="1327351718518"/></attribute>
<attribute name="Attribute_author"><string value="KEIRAN CONROY"/></attribute>
<attribute name="fw_uid"><string value="76a7c0ec-64af-4362-9644-ec4fa8675b88"/></attribute>
<attribute name="updateddate"><date value="2014-06-02 20:10:08.708"/></attribute>
<attribute name="status"><string value="ED"/></attribute>
<attribute name="subtype"><string value="Article"/></attribute>
<attribute name="updatedby"><string value="fwadmin"/></attribute>
<attribute name="createdby"><string value="fwadmin"/></attribute>
<attribute name="Group_Category"><assetreference type="ArticleCategory" value="1327351718595"/></attribute>
<attribute name="template"><string value="ArticleLayout"/></attribute>
<attribute name="createddate"><date value="2012-04-27 08:09:29.168"/></attribute>
<attribute name="Attribute_body"><string value="<p class="ckbody">
	Set two cones 10 yards apart and do the following:<br />
	<br />
	Start by facing forward in a staggered stance. On &quot;go,&quot; sprint to the opposite cone. At the cone, regain control, stop as quickly as possible and backpedal to the start.</p>
<p class="ckbody">
	At the start, turn your hips, plant your outside foot and begin side shuffling back to the far cone. When you reach the cone, plant your outside foot again and shuffle back to the starting cone.</p>
<p class="ckbody">
	Repeat the same sequence using carioca footwork (side step, crossover step, side step, crossover behind). After returning to the cone on the last carioca step, plant and sprint past the last cone.</p>
<p class="ckbody">
	The whole drill (seven changes) should be completed in 20 seconds or less depending on the age of the athlete. Rest 30-45 seconds and repeat. See how quickly you can change directions, as well as movements.</p>
<p class="ckbody">
	<span id="_CSEMBEDTYPE_=inclusion&amp;_PAGENAME_=avisports%2FAVIImage%2FSummary&amp;_ADDITIONALPARAMS_=thumbnail-field%3D&amp;_cid_=1327351718361&amp;_c_=AVIImage&amp;_deps_=exists"><i>[Asset Included(Id:1327351718361;Type:AVIImage)]</i></span></p>
<p class="ckbody">
	&nbsp;</p>
"/></attribute>
<attribute name="name"><string value="Agility Drills"/></attribute>
<attribute name="Attribute_postDate"><date value="2012-03-05 16:08:13.0"/></attribute>
<attribute name="Attribute_relatedImage"><assetreference type="AVIImage" value="1327351718361"/></attribute>
<attribute name="Publist"><array>
<integer value="1322052581735"/></array>
</attribute>
<attribute name="Attribute_subheadline"><string value="Mastering Changes of Direction "/></attribute>
</asset>
</document>

Such a use of XML export would be an example of a “bad” service — basically forcing the calling application to both know that there might be embedded assets nested within a single attribute (a key feature of WCS, btw) but also forcing this calling application to be responsible for interpreting such embedding and for making subsequent calls to fetch the embedded content, leading ultimately to a chatty application.

Fortunately, a straightforward solution exists: create a simple WCS JSP Template that “evaluates/renders” the nested/aggregated content using the <render:stream> tag. The result will be that all embedded/nested content will be converted to HTML values rather than references with the benefit that the underlying aggregation defined by the CKEdit attribute is now hidden from the caller. Given that there is a normal WCS Template that is doing the rendering, it is trivial to preview the result as there will be a URL that can request the result. Note: for the purposes of this blog I recommend you specify this Template to be “Element defines a whole HTML page and can be called externally” as shown below:

Screen Shot 2014-06-09 at 1.37.27 PM

Here is the simple JSP code that does the CKEdit attribute aggregation (using AVISports coding pattern):

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"
%><%@ taglib prefix="render" uri="futuretense_cs/render.tld"
%><%@ taglib prefix="insite" uri="futuretense_cs/insite.tld"
%><%@ taglib prefix="ics" uri="futuretense_cs/ics.tld"
%><cs:ftcs>
<render:logdep cid='<%=ics.GetVar("tid")%>' c="Template"/>
<ics:callelement element="avisports/getdata">
	<ics:argument name="attributes" value="body" /> 
</ics:callelement>

<insite:edit field="body" value="${asset.body}" editor="ckeditor" params="{noValueIndicator: 'Enter body ', width: '627px', height: '500px', toolbar: 'Article', customConfig: '../avisports/ckeditor/config.js'}"/>

</cs:ftcs>

Using the following Jumpstart Kit preview URL: http://localhost:9080/cs?pagename=avisports/AVIArticle/AggregatedContent&cid=1330881053255&c=AVIArticle, the (now aggregated) rendering of the body result is:

Screen Shot 2014-06-09 at 1.49.41 PM

So far so good. The above only renders the aggregated part of the content. Most likely we would also need to expose the rest of the content’s metadata for the calling application (e.g. name, description, locale, etc,). Rather than laboriously add individual metadata calls as XML entities, it would be much easier to wrap the above Template in a truly generic/reusable code element that in essence “wrapped” the previous HTML in XML. The following code, packaged as a SiteEntry Wrapper whose cs.contenttype=application/xml (set in the resargs for the SiteEntry) does precisely that:

Screen Shot 2014-06-09 at 1.22.03 PM

<%@   taglib prefix="cs"     uri="futuretense_cs/ftcs1_0.tld"
%><%@ taglib prefix="asset"  uri="futuretense_cs/asset.tld"
%><%@ taglib prefix="ics"    uri="futuretense_cs/ics.tld"
%><%@ taglib prefix="render" uri="futuretense_cs/render.tld"
%><%@ page import="COM.FutureTense.Interfaces.*,
                   COM.FutureTense.Util.ftMessage,
                   COM.FutureTense.Util.ftErrors,
                   java.io.*"
%><cs:ftcs><%--

WRAPPER: XMLExportWrapper
INPUT:   a request for an asset's template during static publishing (must specify using this wrapper in addition to the Template)
OUTPUT:  The asset's template wrapped in XML metadata for consumption by 3rd party apps that need compositional content from WCS
NOTE:    be sure to specify cs.contenttype=application/xml in the resargs for this SiteEntry

--%><%
%><ics:if condition='<%=ics.GetVar("seid")!=null%>'><ics:then><render:logdep cid='<%=ics.GetVar("seid")%>' c="SiteEntry"/></ics:then></ics:if><%
%><ics:if condition='<%=ics.GetVar("eid")!=null%>'><ics:then><render:logdep cid='<%=ics.GetVar("eid")%>' c="CSElement"/></ics:then></ics:if><%

%><asset:load    name="anAsset" type='<%=ics.GetVar("c")%>' objectid='<%=ics.GetVar("cid")%>' editable="true" /><%
%><asset:scatter name="anAsset" prefix="exp" exclude="true" /><%
%><asset:export  name="anAsset" prefix="exp" output="assetXML" writeattrvalue="true" /><%// use false so that the exported XML has same format as below %><%

%><%

	String assetXML   = ics.GetVar("assetXML");
	String beginCDATA = "<![CDATA[";
	String endCDATA   = "]]>";
	String temp1      = assetXML.replace("</asset>", "");
	String temp2      = temp1.replace("</document>", "");

%><%= temp2 %><% // ****** this outputs the default WCS XML metadata of the asset, minus the closing tags. Must be the first line of output! %>

<attribute name="AggregatedHTML"><% // ****** this outputs the aggregated (i.e. Compositional) HTML %>
	<string>
		<%= beginCDATA %>
		<render:satellitepage pagename='<%=ics.GetVar("childpagename")%>' packedargs='<%=ics.GetVar("packedargs")%>'>
				<render:argument name='c'      value='<%=ics.GetVar("c")%>'/>
				<render:argument name='cid'    value='<%=ics.GetVar("cid")%>'/>
				<render:argument name='p'      value='<%=ics.GetVar("p")%>' />
				<render:argument name='d'      value='<%=ics.GetVar("d")%>' />
				<render:argument name="locale" value='<%=ics.GetSSVar("preferred_locale")%>'/>
		</render:satellitepage>
		<%= endCDATA %>
	</string>
</attribute>

</asset><% // be sure to close the XML tags %>
</document><% // be sure to close the XML tags %>
</cs:ftcs>

Using the following Jumpstart Kit preview URL that calls the wrapper: http://localhost:9080/cs?childpagename=avisports/AVIArticle/AggregatedContent&cid=1330881053255&c=AVIArticle&pagename=XMLWrapper, the result is:

Screen Shot 2014-06-09 at 1.51.05 PM

As you can see, the response to the browser is XML (and because of the cs.contenttype-application/xml resarg, the browser interprets it correctly). Also note it includes the aggregated content (the embedded Image asset here simply converted to a “usable” URL) such that the caller does not need to know or care where the other content came from. Note that this simple pattern of using an XMLWrapper will work for ANY rendering Template for ANY assettype! It must be pointed out that the amount of aggregation possible can be anything you want – it doesn’t just have to be to convert embedded assets in a CKEdit attribute, but could include related assets, breadcrumb, navigation, brother/sister assets, etc. The possibilities are limitless here. Basically anything you want to hide from the caller but want to include in the content response for any given asset.

Using this model to Publish XML.

Without belaboring the point, we now have a fairly powerful integration point with WCS. If we wanted to export all the assets as “aggregated” XML it would be fairly trivial to do. Rather than create a custom publish mechanism (a fairly involved exercise that is beyond the scope of this blog post), I’d like to demonstrate a quick and easy way for you to see how it all could work. For the purposes of this blog, let’s resurrect the deprecated Static Publishing mechanism to demonstrate this. To make Static Publishing work with 11.1.1.8 you need to update your futuretense.ini property file:

advancedUI.enableAssetForms=true

Changing this setting from false (the default as of 11.1.1.8) to true will allow you to set Starting Points and other needed features to gain access to the deprecated Static Publishing mechanism.

Once in your Admin Screen, go to Publishing and create a Static Publishing Destination:

Screen Shot 2014-06-09 at 2.01.28 PM

You’ll need to create and set a Starting Point (you can read much more in the Admin Guide, so I won’t go into the details here). But a typical Starting Point would be a Page asset with a Template that has a list of links to Articles. In effect, with a Starting Point you are creating effectively a sitemap (or subset therein) that the system will use to “crawl” the Static Publishing looking for new pages to publish. For our demo we can use our Article as a Starting Point. Note that here we are specifying the XMWrapper as a wrapper (as shown in the screenshot below) — the result is, as you might have guessed, that our asset will export static, aggregated XML.

Screen Shot 2014-06-09 at 2.04.16 PM

NOTE: The exported static files will have a .html extension (apparently hardwired into Static Publishing), but they are indeed XML.

Add Your Comment