Thursday, September 10, 2015

Sample url assembler for avisports

Before introduction of Vanity URL in Oracle WCS 11g 11.1.1.8.0 version, writing custom assemblers was only the official way to change the generic long url to pretty url.

With installation of FatWire/Oracle WCS from scratch, there are basically 2 URL assemblers present in the product itself: Query Assembler (default url assembler) and QueryAsPathInfo Assembler (appends url to query string, after encoding it, to the servlet name; which can be indexed by search engines). Furthermore, if you have installed FirstSiteII as sample site, then FSIIAssembler is already present in /web-apps/WEB-INF/lib folder with name as firstsite-uri.jar. Also, source code of this assembler can be found in unpacked WCS installation package here under FirstSiteII\PrettyUrl folder.

Even after introduction of Vanity URLs, few clients prefer to have assembler than Vanity urls. For e.g. Think of the case wherein client wants to have certain string in the url which is not present in any of WCS assets. Even if the desired string were present in some other asset, for e.g. if corresponding metadata title from metadata flex family is to be added in url for every product asset, one has to write filter to achieve it. Furthermore, introduction of filters meaning updation of mass assets which may not desirable for few. Also, if a project was upgraded from old version to 11g 8.0 version, many clients don't like to configure vanity urls as it is arduous task to add vanity urls manually (considering it is not possible with automatic vanity url pattern configuration).

Although FirstSiteII url assembler is present, I thought to create my own url assembler as an exercise as nearly every project has a requirement to do so. Before that, let me put some very simple pointers on how URL assemblers work, taking FirstSiteII assembler as an example.

The FirstSiteII assembler creates URLs for
page asset as: <servlet>/<site>/<siteprefix>/<pageid>
all other asset types as: <servlet>/<site>/<siteprefix>/<assettype>/<assetid>/<pageid>

where,
<servlet> can be ContentServer or Satellite
<site> is extracted from 1st part of "/" from childpagename as childpagename=FirstSiteII/FSIILayout
<siteprefix> is extracted from the pagename as pagename=FSIIWrapper. For eg: here <siteprefix> is FSII
<assettype> == c parameter
<assetid> == cid parameter
<pageid> == p parameter

If any parameter is missing, next assembler is called which is usually configured to QueryAssembler. Assembler rankings are configured in ServletRequest.properties file. If you want to run FSIIAssembler, then proceed with updating FSIILink template for all asset types and add assembler="fsii" param to <render:gettemplateurl> tag. Verify once,  your products urls should look like the following:
http://<hostname>:<port>/cs/Satellite/FirstSiteII/FSII/Product_C/<ProductCId>/<ParentPageid>

Furthermore, FSIIAssembler can be configured to show names rather than cid and p which is nicely explained in this post.

Main method of assembling & disassembling the url is valueOf([AssemblerName] assembler, Definition def) where all params are extracted from definition object and assembled into one url, and disassembled when pretty url is requested on browser, respectively. If you keep most of the code as is  from FSIIAssembler and update only these 2 methods and few params here and there, it would be sufficient to code your own url assembler. Also, if you want certain strings to be passed or asset related info, it is better to create separate utility class to retrieve it.

After customising FSIIAssembler, I created my own assembler: AVIAssembler for avisports site and deployed on 11.1.1.6.1 JSK, which creates the following url pattern -
for page assets: <servlet>/<sitename>/Page/<Page name>/<Template>
for article assets: <servlet>/<sitename>/AVIArticle/<Article Parent>/<Article name>/<Template>

and a utility class - AVIAssemblerUtility to get name from cid and vice-versa using Michele's helper class and to get Parent name of Article asset. I've shared this assembler related stuff here.

To make this assembler work with JSK, do the following:
1. Deploy avisports-uri.jar file to /web-apps/cs/WEB-INF/lib folder
2. Make backup of ServletRequest.properties. Register url assembler by updating in ServletRequest.properties and make avisports as 1st rank. Note: If you are ranking AVISports as 1st, then keep FSIIAssembler as 2nd and Query Assembler as 3rd rank. Update each FSIILink template to include assembler="fsii" to run FSII urls, but still will fail to run product, product parent and content urls. Hence, later you can restore ServletRequest.properties from backup to run FirstSiteII and avisports as it was running earlier without AVIAssembler.

Add the following 2 lines and update rankings of other assembler accordingly:
uri.assembler.1.shortform=avi
uri.assembler.1.classname=com.sites.avisports.uri.AVIAssembler

and restart your JSK
3. Update the link CSElements (GetLink) which generate urls for page and article assets by adding assembler="avi"
4. Update the element - /Head to point to correct relative path to static folder - avisports within JSK.
5. Test your URLs

Page urls should be like http:localhost:8080/cs/Satellite/avisports/Page/Home/HomeLayout1

and article urls should be like the following: http://localhost:8080/cs/Satellite/avisports/AVIArticle/Surfing/How+Dial+A+Spot/ArticleLayout



Further improvements can be done like:
1. Generate path and pass it as a argument to <render:gettemplateurl> tag and then, include that path in the assembler rather than asset type name and site name. For e.g. As Running page asset is child page asset under Home page, url should be http://localhost:8080/cs/Satellite/Home/Running/SectionLayout rather than http://localhost:8080/cs/Satellite/avisports/Page/Running/SectionLayout

if article clicked from any page, it should be
http://localhost:8080/cs/Satellite/Home/<SurfingPageName>/<ArticleParent>/How+Dial+A+Spot/ArticleLayout
rather than as it is configured with this assembler http://localhost:8080/cs/Satellite/avisports/AVIArticle/Surfing/How+Dial+A+Spot/ArticleLayout

2. One can also remove /cs/Satellite from the URL by rewriting url using mod_rewrite.

Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

Monday, September 7, 2015

Content targeting based on clickstream

Every online shopping store has a feature where consumers are shown related items or recommended items as any registered or anonymous users keep on browsing certain items on a website. Such use case are very common and can be developed within WCS or FatWire using Engage.

I am describing a very simple use case using Oracle WCS 11g 11.1.1.6.1 JSK for avisports site. My aim is to record number of times any article (article can be of any type: Running, Football, Skiiing, etc.) was clicked on a particular section and show recommended articles in "you may also like" section on basis of number of clicks + article type.

 Solution can be broken into following steps:
  • Enable visitor attribute, history attribute, history definition, segment and recommendation asset type for AviSport site
  • Add all the required start menus
  • Create visitor attribute - aviarticleuser (string, length-20)
  • Create history attributes - AviArticleType (enumeration - with all the values of ArticleCategory attribute: Category) and AviArticleClickCount (integer)
  • Create one history definition - AviArticleHistory (include AviArticleType and AviArticleClickCount)
  • Create segments for each article category or just create for 2 article category with condition aviarticleuser=<articlecategoryname> (For eg: for baseball segment, add the visitor attribute condtion as aviarticleuser=Baseball and for history definition condition, include count >10 and select "Baseball" as AviArticleType)
  • Create one recommendation asset (mode: recommendation, for asset type: article and select all segments and include corresponding articles and also select some articles for default case)
  • Record anonymous or visitor's clicks at particular section only.
  • Update ArticleLayout to calculate segments and show related articles based on recommendations.

First 7 steps, should be very straightforward. Full details on creating and configuring them is already given in developer's guide.

For recording visitor's click on particular section, I have added one param to Summary/Link (engage = yes) as I would be targeting those visitors who click on Related links section's articles only on any article detail page. Then, add engage parameter to avisports/AVIArticle/GetLink. On clicking article link of related links section, will add one param: engage to the output url, so add that param to ArticleLayout and ArticleLayout2 templates in cache criteria.

Now, update both article layout template to call the element: avisports/getVisitorArticles. This element finds the category of the current article (using query),  then finds the history count (vdm:gethistorycount), records the current article (vdm:recordhistory) to history definition, sets scalar values (vdm:setscalar) which helps while calculating segments, calculates segments (commercecontext:calculatesegments) and then loads list of articles from via recommendation asset (commercecontext:getrecommendations). This whole process of how segments are calculated and recommendations work is well explained in detail in developer's guide.

Futher improvements can be done by calculating segments for the visitor and then calling the template by including the segment id in cache criteria and then loading list of assets from recommendations.

As history definition table is created separately and keeps on adding data exponentially as visitors browse your site, it is better to clean data from this table on timely basis which is explained on my other post: Flushing visitor's data

All the updated elements are bundled here for downloading. I hope this post helps many developers.

Further improvements can be done by using the tag: <commercecontext:setsegments> if you know what segment does a visitor belongs to and then, can add the segment id to cache criteria to leverage the use of OOTB caching of Sites. Discussion on setting segment prior to calculating recommendation is succinctly explained here.

Disclaimer: Any sample code on this blog is not officially recommended, use at your own risk.

A simple code compare functionality

One of the most important aspect of any development cycle is deployment and while deployment, it is very important to note the changes don...