Showing posts with label WCS. Show all posts
Showing posts with label WCS. Show all posts

Tuesday, 28 April 2015

Caching Store level or Site level widgets.

While creating cachespec.xml, decision about widgets can be either :
  1. Do not cache
  2. Cache separately
  3. Consume within parent servlet ( cache along with parent JSP )
Do not cache
Widgets like MiniShopCartWidget, InventoryWidget, DiscountWidget etc will not be cached. An entry in cachespec.xml with attributes do-not-cache and do-not-consume set to true will be sufficient for these widgets

    <cache-entry>
        <class>servlet</class>

        <name>/Widgets_701/xxx/InventoryAvailability.jsp</name>
        <name>/Widgets_701/Common/Discounts/Discounts.jsp</name>
        <name>/Widgets_701/xxx/Discounts.jsp</name>
        <property name="do-not-consume">true</property>
        <property name="do-not-cache">true</property>
            <cache-id>
            <component id="storeId" type="parameter">
                <required>false</required>
            </component>
        </cache-id>   
    </cache-entry>


Cache separately

Widgets like productRecommendations, CategoryRecommendations etc can be cached separately so that it can be reused across layout JSPs. An entry in cachespec.xml with attribute do-not-consume = true will be sufficient for these widgets 

    <cache-entry>
        <class>servlet</class>
        <name>/Widgets_701/xxxx/BreadcrumbTrail.jsp</name>
        <property name="save-attributes">false</property>
        <property name="ignore-get-post">true</property>
        <property name="consume-subfragments">true</property>
        <property name="do-not-consume">true</property> <!--  cache separately.. -->
        <property name="do-not-cache">false</property>

        <cache-id>
             <component id="DC_storeIdentifier" type="attribute">
                 <required>true</required>
            </component>

           ...
           ...
        </cache-id>
   </cache-entry>

  

Emarketing Widgets 

Emarketing spot widgets are handled separately. Since activities associated with eMarketing spots can be dynamic or static, the decision about whether to cache the widget or not should be done dynamically at run time based on the activities associated with widget. So general recommendation is to set do-not-cache = true and do-not-consume = true and let the marketing component decide whether to cache the widget or not and whether to cache separately or consume along with parent servlet.


Also you need to  include the following metaDataGenerator in cachespec.xml. This metaDataGenerator will make appropriate decision about caching and adding dependency Ids

<metadatagenerator>
     com.ibm.commerce.marketing.cache.EMarketingSpotMetaDataGenerator
</metadatagenerator>

Consuming widgets
Widgets like CatalogEntry widgets can be consumed within the parent layout JSP like CategoryDisplayPage or SubCategoryDisplay pages. 
When a widget is consumed by a parent JSP, no entries were made for the consumed JSP in cachespec.xml. But the dependency id's of the consumed JSP were added directly to the parent JSP cache entry in cachespec.xml. So assuming ProductPage imports PriceDisplay.jsp, price:<productId> dependency id was added to ProductPage. Whenever the price of a product changes, price:<productId> invalidation id will be published by server and hence ProdcutPage cache gets invalidated. 

When a Store is not using commerce composer tooling, IT developer will create the ProductPage and cachespec.xml. So IT developer will know that productPage consumes PriceDisplay widget and hence adds price dependency id's to the ProductPage cache entry.

But with commerce composer tool, business user has full control over layout widgets and can pick and choose the widgets which goes into layout. So during Code and UT phase, IT developer will have no idea about widgets present in a page and hence cannot add dependency Id's directly to the parent servlet.

For example, assume ProductPage contains price widget and hence developer adds price dependency id to ProductPage entry. Now for some reason, business user removes price widget from the ProductPageLayout widget and adds inventory widget in that slot. Now what happens when price changes ? The entire productPage gets invalidated, even though it doesn't display price. What happens when inventory changes? The page will still be served from cache and it doesn't display the updated inventory status. We cannot expect business user to take help of developers to modify the cachespec.xml as and when he changes the layout widgets.

The answer to this issue is <wcpgl:pageLayoutWidgetCache/> JSTL tag. When this tag gets executed, it adds the dependency id's to the parent servlet cache entry dynamically.

Follow these 3 high level steps to consume the widget with the parent servlet:
  1. Add <wcfpgl:pageLayoutWidgetCache/> to the main widget JSP.
  2. Make an entry in cachespec.xml for the widget JSP. Add all the cache-ids and dependency ids for the entry
  3. Set do-not-consume = true and do-not-cache = true.
Now if the widget is included in the layout by business user, then during runtime, the widget JSP gets executed. Since we have <wcpgl:pageLayoutWidgetCache/> tag inside the widget JSP, this tag gets executed. This tag reads the dependency ids from the cachespec.xml and sets it on the parent servlet. It also resets do-not-consume and do-not-cache values to false.

If the widget is not included in the layout by business user, then widget JSP doesn't get executed and parent page will not contain any additional dependency IDs.

So far so good. But what happens if someone wants to genuinely set do-not-consume and do-not-cache to true. How does someone tell that the values set in cachespec.xml shouldn't be modified by the included <wcfpgl:pageLayoutWidgetCache/> tag. This is where special dependency id ignoreDoNotConsume comes into picture. So if you want to let the pageLayoutWidgetCache tag to reset the do-not-consume and do-not-cache values and add dependency id's to the parent servlet, then add this dependency tag to the widget cache entry in cachespec.xml

<dependency-id>ignoreDoNotConsume</dependency-id>

 This special tag tells the pageLayoutWidgetCache handler to ignore the do-not-consume setting on cachespec.xml and reset it to false. If this dependency id is not present, then pageLayoutWidgetCache will not do any additional processing and lets the cache component of application server to handle it appropriately.

Use of pageLayoutCache JSTL tag and PageDesignMetaDataGenerator

Typically when servlet caching is enabled in WCS for Store JSPs, top level browsing pages like ProductDisplay, CategoryDisplay, HomePage are cached. These pages are cached on the input parameters to the view.

For example, ProductDisplay view will be cached on productId or partNumber, storeId, langId, catalogId. Once the page is cached, the next step is to identify the dependency Id's based on the content displayed by the view, so that the cache can be invalidated whenever the content of the page changes. 
So for example, if page displays both offerPrice and listPrice, then dependency Id's will be added for each price, so that even if one of the price changes entire cache gets invalidated.

Prior to Feature Pack 7, the ProductDisplay page was built by IT team and they typically updated the cachespec.xml to add dependency ids, cache attributes etc., based on page contents.

But starting with feature pack 7, business users have ability to use commerce composer to modify the page contents ( add / remove widgets ) as well as schedule the priority, start date / end date for the layouts. Which means, ProductDisplay page dependency Id's cannot be statically added to cachespec.xml during Code and UT phase. The dependency Id's needs to be added dynamically at runtime based on the layout attributes set by business users.

WebSphere Application Server cache module provides a way to programmatically decide whether to cache a page or not and also to add dependency Id's and set cache time out values at run time. This is achieved through MetaDataGenerator class and <metadatagenerator> tag in cachespec.xml.

WCS makes use of this feature to manage the cache properties dynamically at runtime.
 So a new class com.ibm.commerce.pagelayout.cache.PageDesignMetaDataGenerator is created and following tag is added to top level cache entries in cachespec.xml

<metadatagenerator>com.ibm.commerce.pagelayout.cache.PageDesignMetaDataGenerator</metadatagenerator>

PageDesignMetaDataGenerator gets executed for every request. This metaDataGenerator class will identify all the eMarketing Spots and activities responsible for displaying the layout. (Commerce composer uses Marketing runtime under the hood to associate layouts with pageObjects like product pages, category pages, static pages). Once the eMarketingSpots are identified, it checks if these eSpots are static or dynamic in nature. If the eSpots are static then metaDataGenerator decides to cache this page and sets the timeOut value based on the eSpot attributes. If the eSpot is dynamic then metaDataGenerator makes it non cacheable request.

Since business user have the ability to change the layout (add widget, remove widget, change widget properties ) using composer tool, the cache needs to be invalidated dynamically as and when business user changes the layout. 

Communication between composer tooling and layout cache happens through dependency id's and invalidation id's. Whenever business user modifies the layout, pageLayout:<layoutId> dependency id is published by the composer tool.
The cache entry will have pageLayout:<layoutId> as one of it's invalidation id. So when dependency Id pageLayout:<layoutId> is published, the cache gets invalidated.

But how will someone add pageLayout:<layoutId> as invalidation Id to the cache entry ? Obviously it cannot be added statically in cachespec.xml, since layoutId is determined at runtime.

One option is to make use of MetaDataGenerator to identify the layoutId. But as we know, MetaDataGenerator gets executed for every request ( even if request is eventually served from cache ). So identifying the layoutId by executing pageDesign handler in metaDataGenerator for every request is very expensive operation and defeats the whole purpose of caching.

This is where <wcpgl:pageLayoutCache> JSTL tag comes into picture. This tag needs to be included on every layout JSP page. This tag makes use of the pageDesign object available in JSP and retrieves the layoutId from pageDesign object. It adds pageLayout:<layoutId> as one of the dependency id to the cache entry. Since layout details already fetched in the JSP, the tag doesn't make any additional service call. Also when the request is served from cache, the JSP never gets executed and hence there is no overhead with this approach.

So in summary, whenever layout JSP ( the top level JSP, which has an entry in struts-config-ext.xml + makes a call to pageDesign object to retrieve the layout information ) is cached, the following additional steps should be done:

1) Add <metaDataGenerator> tag to the cache entry in cachespec.xml - This tag decides whether to cache the request or not. If cached, adds timeOut value and emsName:<name> as one of the dependency id. This helps to invalidate the cache whenever layout association changes.
2) Add <wcfpgl:pageLayoutCache> into JSP - This tag adds pageLayout:<layoutId> as one of the dependency id, so that when layout design changes, the cache gets invalidated.


Tuesday, 21 April 2015

Invoking REST Services (GET Method ) from Store JSP

WCS provides various resource handlers like CartHandler, OrderHandler, PersonHandler, PromotionHandler etc., Let us explore how to invoke GET request on these resources from one of the Store JSP code.
For illustration purpose let us assume that you want to invoke GET request on CartHandler resource to retrieve shopping cart details. CartHandler resource class is annotated with store/{storeId}/cart and getCart method is annotated with @self as shown in below code snippet.

Cart Handler



Now to invoke this REST Service from a REST Client (HTTP Requester for FF or PostMan for Chrome), you will use the below URL:

URL:
http://localhost/wcs/resources/store/10001/cart/@self?pageNumber=1&pageSize=20
HTTP Method –> GET
JSON Body -> NULL

Note:
wcs/resources is the context path for Rest.war. All REST Handler classes are declared in Rest.war/web-inf/config/providers.properties. web.xml points to this providers.properties file as shown below:

<servlet>
  <servlet-name>JAX-RS Servlet</servlet-name>
  <servlet-class>

       org.apache.wink.server.internal.servlet.RestServlet
  </servlet-class>
  <init-param>
     <param-name>propertiesLocation</param-name>
     <param-value>/WEB-INF/config/rest-config.properties</param-value>
  </init-param>
  <init-param>
     <param-name>applicationConfigLocation</param-name>
     <param-value>

       /WEB-INF/config/providers-ext.properties;
       /WEB-INF/config/providers.properties;/WEB-INF/config/resources-ext.properties;
       /WEB-INF/config/resources.properties
     </param-value>
  </init-param>
  <init-param>
</servlet>



If you want to make use of this GET Cart Service in ShoppingCart.jsp to display the cart details, you have two options:
  1. On page load, make an Ajax call to this REST URL and build the DOM using the returned JSON object and javascript functions.
  2. Use java scriptlets on JSP code with java.net.URL object to open connection to the REST URL and retrieve the response. Then use JSTL tag library to loop through the response and build the DOM.

But if you go with second option, you have to handle exception scenario, connection timeOut issues, server hostName and portNumber configuration, caching etc.,
Luckily WCS framework helps to overcome these issues easily and provides a simpler way of invoking GET REST Services, through wcf:rest JSTL Tag.
The tag handler class RestTag encapsulates all the complexities involved in invoking the REST Service and allows UI developers to concentrate on presentation layer.

Here is the sample code to invoke the GET Service using wcf:rest tag

<wcf:rest var="myOrders" url="store/{storeId}/cart/@self">
      <wcf:var name="storeId" value = “10001" encode="true"/>
      <wcf:param name="pageSize" value="20"/>
      <wcf:param name="pageNumber" value="1"/>
</wcf:rest>
 
REST tag builds the final URL by:
  1. Converting relative URL to full URL. It looks at REST_CONFIG variable set in EnvironmentSetup.jspf file as request attribute to identify the port number, server host name etc.,
  2. Replaces variables in the URL with value passed with wcf:var tag. ( {storeId} will be replaced with 10001 ) in above example.
  3. Any value set with wcf:param tag will be passed as query parameters.
So final URL will be:
http://localhost/wcs/resources/store/10001/cart/@self?pageSize=20&pageNumber=1

So by using wcf:rest tag, you can easily invoke any GET Rest request from your JSP code.

Few other benefits of REST tag are:
  1. REST Tag also takes care of passing headers (WCToken, WCTrustedToken, Cookies etc) as part of the request.
  2. REST Tag implements caching at request level. So when GET request to same resource is invoked second time within the same request, the response comes from cache.
  3.  REST Tag takes care of Local binding / Remote binding setting done in component configuration files. So if binding is Local (Stores.war runs within WC.ear), request dispatcher is used to invoke the REST call instead of opening URL connection.

Invoking Controller Commands from Store JSP



WCS business logic layer provides various artifacts like Controller Commands, SOI Services, BOD (SOA) Services and REST Services to execute business logic. These commands and services can be invoked from view layer (For ex: Web Store) using Struts, JSTL tag libraries and WCS framework. Struts is used for managing view – controller interaction. In this post we will see how to invoke controller command from a Store JSP.

 Assume you have created a custom command implementation com.mycompany.MyOrderItemAddCmdImpl for com.ibm.commerce.orderitems.commands.OrderItemAddCmd, to add an item to shopping cart.

The list below provides high level steps required to invoke this custom command from one of the Store JSP (with StoreId = 10001)

  •  Register this command in CMDREG table by executing following insert statement.
insert into cmdreg values(10001,'com.mycompany.MyOrderItemAddCmdImpl','description','com.ibm.commerce.orderitems.commands.OrderItemAddCmd',null,null,null, 0);
  •  Update struts-config-ext.xml in Stores.war/web-inf folder with following entry

<action parameter="com.ibm.commerce.orderitems.commands.OrderItemAddCmd" path="/MyOrderItemAdd" type="com.ibm.commerce.struts.BaseAction">
<set-property property="credentialsAccepted" value="10001:P "/>
<set-property property="authenticate" value="10001:1"/>
<set-property property="https" value="10001:1"/>
</action>
  
• /MyOrderItemAdd is the mapping name which will be used in the URL to invoke MyOrderItemAddCmdImpl command.
• Set credentialsAccepted value to <storeId>:P, if this command can be executed by partially authenticated user (Remembered user)
• Set authenticate value to <storeId>:1, if this command needs to be executed by only logged in user
•  Set https value to <storeId>:1, if this command needs to be executed over secure HTTPS protocol.
•  Set type = “com.ibm.commerce.struts.BaseAction” if the command needs to be invoked in Web1.0 style. When a controller command is invoked with BaseAction, ‘URL’ query parameter is mandatory. Once the command execution completes, user will be redirected to the view specified with ‘URL’ query parameter.
•  Set type = “com.ibm.commerce.struts.AjaxAction” if the command needs to be invoked in Web2.0 style (Ajax request). When a controller command is invoked with AjaxAction, after execution of controller command, request will be forwarded to AjaxActionResponse.jsp or AjaxActionErrorResponse.jsp, which takes response properties from command, converts to JSON object and returns JSON response to client.

  
  • You can now POST form from JSP to below URL, by setting action attribute of form to /MyOrderItemAdd.
https://localhost/webapp/wcs/stores/servlet/MyOrderItemAdd?catentry_id=12345&quantity=1&URL=ShoppingCartPageView&errorViewName=ErrorPageView


Tracing the request:
webapp/wcs/stores maps to Stores.war. So request is handled by Store Webapp.

ECActionServlet is configured to handle incoming requests with path /servlet/* in web.xml and is the entry point for the request.

BaseAction / AjaxAction handles the request and reads struts-config-ext.xml entry and looks for /MyOrderItemAdd

MyOrderItemAdd maps to interface name in struts-config-ext.xml.

CMDREG contains the implementation class for this interface name.

MyOrderItemAddCmdImpl command gets invoked.

Query Parameters / Form post parameters are set into RequestProperties by base 
ControllerCommandImpl and will be available for the command to execute the business logic.


ResponseProperties will be set after successful execution of the command.