Friday 30 September 2016

ATG Endeca Integration | Write log [Logging API] requests while using Assembler API

In the case we want to use endeca logging and reporting. The first thing is we need to write log entries in LogServer's log file. Here I am going to explain, how to write log request to log file with assembler API.


Below are steps : 

1. Create class which extends RequestEventListener.
2. Create global scoped component of this class.
3. Add this component in assemblerEventListeners property of NucleusAssemblerFactory.

Below is the example : 

1. Create class which extends RequestEventListener.

=========================================================================
package com.shop.endeca.assembler.logserver;

import atg.servlet.ServletUtil;
import com.endeca.infront.assembler.ContentItem;
import com.endeca.infront.assembler.event.request.RequestEvent;
import com.endeca.infront.assembler.event.request.RequestEventListener;
import com.endeca.infront.navigation.event.NavigationEventWrapper;
import com.endeca.logging.*;
import atg.nucleus.logging.ApplicationLogging;
import atg.nucleus.logging.ClassLoggingFactory;


public class LogServerAdapter extends RequestEventListener
{

    private static ApplicationLogging mLogger =     ClassLoggingFactory.getFactory().getLoggerForClass(LogServerAdapter.class);
   
    private static LogConnection Logconn;

    private LogConnection getLogconn() {
        Logconn=new LogConnection(getLogServer(),getPortNumber());
        return Logconn;
    }

    public void setLogconn(LogConnection conn) {
        this.Logconn =Logconn;
    }
   
    // Endeca server hostname where endeca log server is hosted.
    private String logServer;

    public  String getLogServer() {
        return logServer;
    }

    public void setLogServer(String logServer) {
        this.logServer = logServer;
    }

    // Endeca Log Sever port number
    private Integer portNumber;


    public Integer getPortNumber() {
        return portNumber;
    }

    public  void  setPortNumber(Integer portNumber) {
        this.portNumber = portNumber;
    }

    /**
     * @return ApplicationLogging object for logger.
     */
    private ApplicationLogging getLogger() {
        return mLogger;
    }


    @Override
    public void handleAssemblerRequestEvent(RequestEvent assemblerReqEnt, ContentItem conItemPar) {
       
        try
        {
            LogEntry handleLog = new LogEntry();
            Logconn=getLogconn();
           
           NavigationEventWrapper eventNavigation = new NavigationEventWrapper(assemblerReqEnt);
            if (assemblerReqEnt.getSessionId() != null) {
                handleLog.putString("SESSION_ID", assemblerReqEnt.getSessionId());

            }


            if (eventNavigation.getAutocorrectTo() != null) {
                handleLog.putString("AUTOCORRECT_TO", eventNavigation.getAutocorrectTo());
            }


            if (eventNavigation.getDimensions() != null) {
                handleLog.putList("DIMS", eventNavigation.getDimensions());
            }


            if (eventNavigation.getDimensionValues() != null) {
                handleLog.putList("DVALS", eventNavigation.getDimensionValues());
            }


            if (eventNavigation.getEneTime() != null) {
                handleLog.put("ENE_TIME", eventNavigation.getEneTime());
            }


            if (eventNavigation.getNumRecords() != null) {
                handleLog.put("NUM_RECORDS", eventNavigation.getNumRecords());
            }

            if (eventNavigation.getRecordNames() != null) {
                handleLog.putList("RECORD_NAMES", eventNavigation.getRecordNames());
            }


            if (eventNavigation.getRequestType() != null) {
                handleLog.putString("TYPE", eventNavigation.getRequestType().toString());
            }


            if (eventNavigation.getSearchKey() != null) {
                handleLog.putString("SEARCH_KEY", eventNavigation.getSearchKey());
            }

            if (eventNavigation.getNumRefinements() != null) {
                handleLog.put("NUMREFINEMENTS", eventNavigation.getNumRefinements());
            }


            if (eventNavigation.getSortKeys() != null) {
                handleLog.putList("SORT_KEY", eventNavigation.getSortKeys());
            }


            if (eventNavigation.getSpotlights() != null) {
                handleLog.putList("MERCH_RULES", eventNavigation.getSpotlights());
            }


            if (eventNavigation.getSearchMode() != null) {
                handleLog.putString("SEARCH_MODE", eventNavigation.getSearchMode());
            }


            if (eventNavigation.getSearchTerms() != null) {
                handleLog.putString("SEARCH_TERMS", eventNavigation.getSearchTerms());
            }


            Logconn.logAsynchronously(handleLog);


            if(getLogger().isLoggingDebug())
            {
                getLogger().logDebug("reporting log is updated.");
            }

        }
        catch (LogException e)
        {
            if (getLogger().isLoggingError())
            {
                getLogger().logError("There is some LogException"+e.getMessage());
            }
        }catch(Exception e)
        {
            if (getLogger().isLoggingError())
            {
                getLogger().logError("There is some other exception"+e.getMessage());
            }
           
        }
    }
}

=========================================================================

2. Create global scoped component of this class.

=========================================================================
 # /atg/endeca/assembler/logserver/LogServerAdapter
$class=com.shop.endeca.assembler.logserver.LogServerAdapter

$scope=global
portNumber=16010
logServer=localhost

=========================================================================

3. Add this component in assemblerEventListeners property of NucleusAssemblerFactory.

=========================================================================
 assemblerEventListeners+=\
    /atg/endeca/assembler/logserver/LogServerAdapter

=========================================================================

Wednesday 14 September 2016

Resolved | Workbench Error org.apache.sling.api.SlingException

Today while accessing Oracle Commerce Workbench got below exception on browser.
=========================================================================
The requested URL /ifcr/sites/Store.html resulted in an error in /apps/endeca/site/html.jsp. Exception: org.apache.sling.api.SlingException: at org.apache.sling.scripting.jsp.jasper.servlet.JspServletWrapper.handleJspExceptionIntern al (JspServletWrapper.java:560) atorg.apache.sling.scripting.jsp.jasper.servlet.JspServletWrapper.handleJspException (JspServletWrapper.java:496)
=========================================================================

Solution : Restart the Endeca ToolsAndFrameworks

Wednesday 7 September 2016

ATG BCC | Disable(hide) Promotion Template in template selection screen

Sometimes business is not utilizing all the available promotion templates in BCC. So it is better to hide these non-required templates in template selection screen in BCC.

Here are the steps : 


1. Copy pmdt file for promotion to disable in \atg\registry\data\promotiontemplates\item directory of "Versioned" module of your application.You can find existing templates at DCS\config\config.jar\config\atg\registry\atg\registry\data\promotiontemplates.

2. Add available-in-ui="false" attribute in ui-description tag of pmdt.

3. Rebuild and Redeploy the BCC ear. Now this template won't be available in template selection screen.

In below pmdt  SpendY GetGiftWithPurchase promotion is disabled.

==========================================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE template PUBLIC "-//Art Technology Group, Inc.//DTD PMDT 1.0//EN" "http://www.atg.com/dtds/pmdt/pmdt_1.0.dtd">
<template item-type="Item Discount"
                    author="Sean McIntyre"
                    last-modified-by="Sean McIntyre"
                    creation-date="11/02/2011">
  <ui-description display-name-resource="template.gwp.spendYGetGiftWithPurchase.title" 

             resource-bundle="atg.remote.promotion.template.Resources" available-in-ui="false">
  </ui-description>
 </template>

==========================================================================

Sunday 4 September 2016

ATG | Internationalizing the repository items

Here I am going to explain internationalization "best-practice" strategy provided by ATG.

Here are the steps :

1. Identify properties for translation in given item-descriptor. In below example title,content, location.

2. Now create new properties in base item-descriptor [article]. These properties will point to existing properties database column. For example we have created contentDefault property to refer long_description column. This column was initially referred by content property. Name these properties in such a way so that these can bee easily differentiate from derived property [postfix property name with Default].

3. Create the helper item descriptor to store the translated data. This item-descriptor will contain all the translated properties from main item-descriptor. Here articleTranslation used for this purpose.

4. Now add map type property in base item-descriptor which is map of locale to translated item [defined in above step]. In below example translations property is used to map locale to articleTranslation.

5. Change the definitions of the translatable properties in the existing item types. The new definitions should specify that each translatable property is a derived property. Here atg.repository.dp.LanguageTranslation class used to implement the derivation.

Below mechanism is used to find the values of translatable property.
  •  Use the current locale to look up a corresponding baseTypeTranslation [articleTranslation] item in the translations property map. The property derivation attempts to find a best match. First, it searches the locale keys for a match on the entire locale with a variant, then it searches for a match on the locale without a variant, and finally it searches on just the language code.
  •  If a baseTypeTranslation [articleTranslation] item exists for the current locale, use its value for the property.
  • If a baseTypeTranslation [articleTranslation] item does not exist for the current locale, or its value for the property is null, use the translatablePropertyDefault [stored in base item-descriptor] value instead.
Below is the example of internationalization : Here we have article repository item [with 3 properties title,content, location]. Here articleTranslation item-descriptor is used to translate these properties.
==========================================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<gsa-template>

    <!--///////////////////////////////-->
    <!--     article                         -->
    <!--///////////////////////////////-->
   
    <item-descriptor name="article" display-property="titleDefault" display-name="article">
   
        <table name="shop_article" type="primary" id-column-name="article_id">
            <property name="id" data-type="string" column-name="article_id"  display-name="article_id"/>
            <property name="titleDefault" data-type="string" column-name="title" 

                     display-name="titleDefault"/>   
            <property name="contentDefault" data-type="big string" column-name="long_description" 

                    display-name="contentDefault"/>
            <property name="locationDefault" data-type="string" column-name="article_location" 

                    display-name="location"/>   
        </table>
       
        <property name="title" data-type="string" writable="false" hidden="true" queryable="true" 

                    display-name="title">
            <derivation user-method="atg.repository.dp.LanguageTranslation">
                <expression>titleDefault</expression>
            </derivation>
            <attribute name="defaultProperty" value="titleDefault" />
            <attribute name="defaultLocale" value="en" />
        </property>
       
        <property name="content" data-type="big string" writable="false" hidden="true" queryable="true"
            display-name="content">
            <derivation user-method="atg.repository.dp.LanguageTranslation">
                <expression>contentDefault</expression>
            </derivation>
            <attribute name="defaultProperty" value="contentDefault" />
            <attribute name="defaultLocale" value="en" />
        </property>
       
        <property name="location" data-type="string" writable="false" hidden="true" queryable="true"
            display-name="location">
            <derivation user-method="atg.repository.dp.LanguageTranslation">
                <expression>locationDefault</expression>
            </derivation>
            <attribute name="defaultProperty" value="locationDefault" />
            <attribute name="defaultLocale" value="en" />
        </property>
       
        <table name="shop_article_translations" type="multi" multi-column-name="locale" 

                     id-column-names="article_id">
            <property name="translations" display-name="Translations" 

                    column-name="translation_id"      data-type="map"  
                    component-item-type="articleTranslation"  display-name="translations">
            </property>
        </table>
       
    </item-descriptor>
   
    <!--///////////////////////////////-->
    <!--     articleTranslation      -->
    <!--///////////////////////////////-->

    <item-descriptor name="articleTranslation" id-space-name="articleTranslation" 

         display-name="articleTranslation"  display-property="title">
       
        <table name="shop_article_xlate" type="primary" id-column-name="translation_id">
            <property name="title" column-name="title" data-type="string"  display-name="Title" />
            <property name="content" data-type="big string" column-name="long_description" 

                      display-name="content"/>
            <property name="location" data-type="string" column-name="article_location"

                     display-name="location" />
        </table>
       
    </item-descriptor>
</gsa-template>

==========================================================================

Advantages of this approach : 
  • Applications can switch between international and non-international modes without requiring any JSP  page changes. 
  • No database schema changes are required to add additional languages.