Saturday 25 July 2015

ATG-Endeca Integration | Property 'contentItem' not found on type atg.servlet.DynamoHttpServletRequest

While trying to access contentItem returned by InvokeAssembler. Got below exception trace on server console.
========================================================================
javax.el.PropertyNotFoundException: Property 'contentItem' not found on type atg.servlet.DynamoHttpServletRequest
        at javax.el.BeanELResolver$BeanProperties.get(BeanELResolver.java:193)
        at javax.el.BeanELResolver$BeanProperties.access$400(BeanELResolver.java:170)
        at javax.el.BeanELResolver.property(BeanELResolver.java:279)
        at javax.el.BeanELResolver.getValue(BeanELResolver.java:60)
        at javax.el.CompositeELResolver.getValue(CompositeELResolver.java:54)
        at org.apache.el.parser.AstValue.getValue(AstValue.java:118)
        at org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:186)
        at org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:925)
        at org.apache.jsp.cartridges.ContentSlot.ContentSlot_jsp._jspx_meth_dsp_005fgetvalueof_005f0(ContentSlot_jsp.java:255)
        at org.apache.jsp.cartridges.ContentSlot.ContentSlot_jsp._jspx_meth_dsp_005fpage_005f1(ContentSlot_jsp.java:197)
        at org.apache.jsp.cartridges.ContentSlot.ContentSlot_jsp._jspService(ContentSlot_jsp.java:107)
        at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
        at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:369)
        at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:322)
        at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:249)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:638)
        at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:543)
        at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:480)
        at atg.servlet.WrappingRequestDispatcher.include(WrappingRequestDispatcher.java:123)
        at atg.taglib.dspjsp.IncludeTag.doEndTag(IncludeTag.java:883)
        at atg.taglib.dspjsp.DelegatingTag.doEndTag(DelegatingTag.java:147)
        at atg.taglib.dspjsp.AbstractRenderContentItem.doEndTag(AbstractRenderContentItem.java:234)
        at atg.taglib.dspjsp.DelegatingTag.doEndTag(DelegatingTag.java:147)
        at org.apache.jsp.index_jsp._jspx_meth_dsp_005frenderContentItem_005f0(index_jsp.java:410)
        at org.apache.jsp.index_jsp._jspx_meth_c_005fif_005f0(index_jsp.java:381)
        at org.apache.jsp.index_jsp._jspx_meth_dsp_005fpage_005f1(index_jsp.java:203)
        at org.apache.jsp.index_jsp._jspService(index_jsp.java:112)
        at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
        at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:369)
        at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:322)
        at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:249)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at atg.service.configuration.ResponseWrappingConfiguration.invokeFilterChainWithPossibleWrapping(ResponseWrappingConfiguration.java:458)
        at atg.servlet.pipeline.TailPipelineServlet.service(TailPipelineServlet.java:174)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.servlet.pipeline.DispatcherPipelineServletImpl.service(DispatcherPipelineServletImpl.java:275)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.endeca.assembler.AssemblerPipelineServlet.service(AssemblerPipelineServlet.java:458)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.servlet.http.CookieBufferServlet.service(CookieBufferServlet.java:119)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.userprofiling.ExpiredPasswordServlet.service(ExpiredPasswordServlet.java:383)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.servlet.pipeline.MimeTyperPipelineServlet.service(MimeTyperPipelineServlet.java:228)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.droplet.DropletEventServlet.service(DropletEventServlet.java:696)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.commerce.order.CommerceCommandServlet.service(CommerceCommandServlet.java:150)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.commerce.promotion.PromotionServlet.service(PromotionServlet.java:213)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.userprofiling.AccessControlServlet.service(AccessControlServlet.java:696)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.servlet.sessionsaver.SessionSaverServlet.service(SessionSaverServlet.java:2452)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.userprofiling.PageEventTriggerPipelineServlet.service(PageEventTriggerPipelineServlet.java:191)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.search.servlet.SearchClickThroughServlet.service(SearchClickThroughServlet.java:418)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.multisite.SiteSessionEventTriggerPipelineServlet.service(SiteSessionEventTriggerPipelineServlet.java:161)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.userprofiling.SessionEventTrigger.service(SessionEventTrigger.java:512)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.userprofiling.ProfilePropertyServlet.service(ProfilePropertyServlet.java:230)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.userprofiling.ProfileRequestServlet.service(ProfileRequestServlet.java:461)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.servlet.pipeline.DynamoPipelineServlet.service(DynamoPipelineServlet.java:491)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.servlet.pipeline.URLArgumentPipelineServlet.service(URLArgumentPipelineServlet.java:298)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.servlet.pipeline.PathAuthenticationPipelineServlet.service(PathAuthenticationPipelineServlet.java:392)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.servlet.security.ThreadUserBinderServlet.service(ThreadUserBinderServlet.java:113)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.dtm.TransactionPipelineServlet.service(TransactionPipelineServlet.java:234)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.servlet.pipeline.SecurityServlet.service(SecurityServlet.java:196)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.multisite.SiteContextPipelineServlet.service(SiteContextPipelineServlet.java:441)
        at atg.servlet.pipeline.PipelineableServletImpl.passRequest(PipelineableServletImpl.java:157)
        at atg.servlet.pipeline.HeadPipelineServlet.passRequest(HeadPipelineServlet.java:1252)
        at atg.servlet.pipeline.HeadPipelineServlet.service(HeadPipelineServlet.java:930)
        at atg.servlet.pipeline.PipelineableServletImpl.service(PipelineableServletImpl.java:272)
        at atg.filter.dspjsp.PageFilter.innerDoFilter(PageFilter.java:348)
        at atg.filter.dspjsp.PageFilter.doFilter(PageFilter.java:206)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
        at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
        at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
        at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
        at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
        at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:829)
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:598)
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
        at java.lang.Thread.run(Thread.java:662)


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

Here is the solution to this issue.


Configure below listener in the web.xml of your web application.

========================================================================
  <listener>
          <listener-class>atg.taglib.dspjsp.RegisterELResolverServletContextListener</listener-class>
    </listener>

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

To use this listener your web application must use the servlet 2.5 schema or greater.
 

Saturday 11 July 2015

ATG REST MVC Overview

Nowadays it is very common for enterprise applications to share data and business logic with other applications. This can be easily achieved using web services. This becomes very tricky when providing omni channel support in E-commerce (accessing site using android or any other native application).

ATG allows developers to create their own webservices along with pre-packages services.

These pre-packages services are available in below modules.
  1. DAS.WebServices
  2. DPS.WebServices
  3. DCS.WebServices 
ATG supports two types of REST webservices (webservices APIs).
  1. Legacy REST API
  2. REST MVC API
 

Here I am going to explain REST MVC.

Steps to create New REST MVC Call
  • Create Actor.
  • Define Actor chain(s) for that actor.
  • Register Actor with ActorChainRestRegistry.
  • Create Bean filter (optional).
Steps to create REST Actor
  1. Create Component of atg.service.actor.ActorChainService class.
  2. Define actor chains for this component (xml configuration).
  3. Point definitionFile property of the component to xml file created in step 2.
Below is the example of Hello World Actor

=======================================================================
#/com/test/web/actor/HelloWorldActor.properties
$class=atg.service.actor.ActorChainService
definitionFile=/com/test/web/actor/helloWorldActor.xml
=======================================================================

Actor Chain definition file (helloWorldActor.xml)
 
=======================================================================
<?xml version="1.0" encoding="UTF-8"?>
<actor-template default-chain-id="sayHello" xsi:noNamespaceSchemaLocation="http://www.atg.com/xsds/actorChain_1.0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<actor-chain id="sayHello" transaction="TX_SUPPORTS">
       <component id="sayHello" name="/com/test/web/HelloWorld"
           method="sayHello">
      </component>
 </actor-chain>
</actor-template>
=======================================================================

Registering this actor with ActorChainRestRegistry

To register this actor you need to add the actor path with chain id to registeredUrls property of /atg/rest/registry/ActorChainRestRegistry component.

One important thing to remember here is ,that you are registering actor chain not actor. In the case there are more then one chains defined for that actor you need to register each one here. In  other words you can say each chain ID should be registered separately.

By default, no actors are registered.

In below code snippet we are registering sayHello chain. 
=======================================================================
#/atg/rest/registry/ActorChainRestRegistry.properties
registeredUrls=\
         /com/test/web/actor/HelloWorldActor/sayHello

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

 ATG REST MVC Supports below Actor Types.
  • Component Actor 
  • Droplet Actor
  • Form Actor
  • JSP Actor
  • Nested Actor
  • Variable Actor 

Filtering in MVC REST

Filtering is used in REST MVC to control the property in the response object. In other words filter is way to configure which properties will be available in the response object. This is to avoid unnecessary data in the response.
 
REST MVC support two types of filters.
  1. Java bean filtering.
  2. Repository item filtering.
Steps to configure filter
  • Layer /atg/dynamo/service/filter/bean/beanFilteringConfiguration.xml 
  • Configure filter in this file
  • Refer this filter in actor chain
  • ATG recommends 3 types of filters
                   Short
                   Summary
                   Detailed


Once filter is defined you can use filter in actor chain using filter-id attribute.

Filter definition example.

======================================================================= 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bean-filtering SYSTEM "dynamosystemresource:/atg/dtds/beanfilter/beanFiltering_1.0.dtd">
<bean-filtering>
<repository name="/atg/userprofiling/ProfileAdapterRepository">
   <item-descriptor name="user">
      <filter id="customDetail" default-include="false">
        <property hidden="false" name="firstName"/>     
        <property hidden="false" name="lastName"/>
      </filter>
   </item-descriptor>
</repository>

</bean-filtering>
======================================================================= 

Using filter in actor chain.  

=======================================================================
 <?xml version="1.0" encoding="UTF-8"?>
<actor-template default-chain-id="summary" xsi:noNamespaceSchemaLocation="http://www.atg.com/xsds/actorChain_1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <actor-chain id="customDetail" transaction="TX_SUPPORTS">
    <component id="profile" name="/atg/userprofiling/Profile" invoke-method-requires-session-confirmation="true" component-var="profile" set-property-requires-session-confirmation="true">
      <output id="profile" filter-id="customDetail" name="profile" value="${profile.dataSource}"/>
    </component>
  </actor-chain>
</actor-template>

=======================================================================  
Note : It is best to define a filter for every object, so that you can control its output. Note that if an object has no filters defined, it will output all properties.

Configure Security in REST MVC 

Once REST call is implemented then It is time to secure it. Security is crucial to avoid unauthorized access. 

Follow below steps to secure rest webservice
  • Create the RuleSetService.
  • Create Access Controller.
  • Add mapping from actor chain to Access Controller in /atg/dynamo/servlet/dafpipeline/AccessControlServlet. 
 
CustomRuleService (Only logged in user can access).

======================================================================= 
#/atg/rest/CustomRuleSetService.properties
$class=atg.targeting.RuleSetService
updatesEnabled=true
rulesFileCheckSeconds=0

# Use must have securityStatus 4 or higher (EXPLICIT-SIGNIN, SECURE-SIGNIN, CERTIFICATE)
ruleSet=<ruleset>\n  <accepts>\n    <rule op\=and tag\="Show">\n      <rule op\=and tag\="Content">\n      </rule>\n      <rule op\=and tag\="Environment">\n        <rule op\=gt>\n          <valueof target\="securityStatus">\n          <valueof constant\="3">\n        </rule>\n      </rule>\n    </rule>\n  </accepts>\n</ruleset>
======================================================================= 

CustomAccessController
 
=======================================================================
#/atg/userprofiling/CustomAccessController.properties
$class=atg.userprofiling.RuleAccessController
enabled=true
# Rules used to determine whether access should be allowed
ruleSetService=/atg/rest/CustomRuleSetService
# URL to redirect to if access is denied
deniedAccessURL=/rest/model/atg/userprofiling/SecurityStatusActor/authenticationRequired
======================================================================= 
 
 AccessControlServlet
 
======================================================================= 
#/atg/dynamo/servlet/dafpipeline/AccessControlServlet.properties
accessControllers=\
   
/com/test/web/actor/HelloWorldActor/sayHello=\
         /atg/userprofiling/CustomAccessController
======================================================================= 
      
ATG REST MVC Key Points
  • Get and Post are supported.
  • Access restriction  by AccessControllerService.
  • Also support implicit objects (session, request).
  • URL syntax http://host:port/rest/model/actor_component/tail.

ATG REST MVC Key Components
  • /atg/rest/Configuration/ 
  • /atg/rest/registry/ActorChainRestRegistry/
  • /atg/dynamo/service/filter/bean/XmlFilterService
  • /atg/dynamo/service/actor/ActorChainValidationService

Saturday 4 July 2015

Enabling the older IR version in MDEX

Every request in newly created Endeca application was throwing ENEException.

Here is the error from Dgraph log.

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

ERROR 06/13/15 14:51:16.438 UTC (1434207076438) DGRAPH {dgraph} Requested output IR version '620' is supported by this release of the software but not enabled. Use the --back_compat flag to enable the older IR version.
WARN 06/13/15 14:51:16.438 UTC (1434207076438) DGRAPH {dgraph} Error processing HTTP exchange 2: Error:[MDEX] Failed to parse URL: '/graph?node=0&offset=0&nbins=10&irversion=620'

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

The issue was due to version mismatch in API and running MDEX.

Solution : Enable backward compatibility in MDEX.

This can be done in two ways.

1.  MDEX configuration window in EAC admin console of the workbench.
  • Stop MDEX.
  • Add --back_compat 620 to the argument. Here is configuration snapshot.



  • Start MDEX.
 2.  Update startup argument in MDEX configuration file [DgraphDefaults.xml].

 <arg>--back_compat</arg>
      <arg>620</arg>