Sunday 21 October 2012

ACC Changes versus dyn/admin changes


Normally we always have a confusion on what is happening behind the scene when a change is made through dyn/admin to that of ACC

Changes made through dyn/admin are not written to the properties file so they will be lost once you restart the server. One pretty common scenario when we tend to change the component property values through dyn/admin is while diagnosing some problem as we may want to enable debug logs and any other types of output messages which can give us more information. 

When changing component property values from ACC, the changes are actually written to the corresponding component properties files and those properties file will go to the config layer with the highest precedence. And in case you want your changes to be written to a particular config layer then you can select that config layer also within ACC from the menu Tools -> Set Update Layer. By default the config layer at <ATG>/home/localconfig is at the highest precedence so if you don't explicitly specify any other config layer, ACC will write your changes at the component path within <ATG>/home/localconfig.

<ATG>/home/localconfig has a CONFIG.properties having the property defaultForUpdates=true which makes it the default for any updates. You can change this default by changing defaultForUpdates to false in <ATG>/home/localconfig/CONFIG.properties and create a CONFIG.properties with defaultForUpdates=true within your modules' config layer so that ACC by default can write your changes at the component path in your module's config layer.

Thursday 23 August 2012

Transactional Pattern to be followed in form handlers

Below is an article that I found very useful for understanding the transactional pattern that need to be followed in form handlers. Thanks to Oracle Knowledge Base :)


Code that modifies ATG Commerce Object must follow a specific transactional pattern to avoid exceptions and dead locks. The transactional pattern is implemented by the form handlers that work with order objects.Same transactional pattern can be followed in other form handlers as well

ATG Commerce form handlers that modify Order objects typically extends atg.commerce.order.purchase.PurchaseProcessFormHandler class. This subclass of atg.droplet.GenericFormHander is an abstract class that implements transaction management through its beforeSet, afterSet, and handler methods.

beforeSet - Called once before any form handler property is set or the handle method is called. It implements the following transactional steps:


If the formhandler's useLocksAroundTransactions property is true (default), obtain a transaction lock before the transaction is created.
This prevents a user from modifying an order in multiple concurrent threads. The lock name used defaults to the current profile ID. For more information, see the API documentation for atg.commerce.util.TransactionLockFactory. (Note that use of locking has a small performance impact.)
Check for an existing transaction and, if no transaction exists, create one.

 public boolean beforeSet(DynamoHttpServletRequest pRequest,
DynamoHttpServletResponse pResponse) throws DropletFormException
{
  try {
    acquireTransactionLock(pRequest);
  }
  catch (DeadlockException de) {

    // We are going to log the exception here and then ignore it because
    // the worst that should happen is that the user will get a concurrent
    // update exception if two threads try to modify the same order, and we
    // can recover from that.

    if (isLoggingError())
      logError(de);
  }

  Transaction t = ensureTransaction();
  if (t != null)
    setTransactionCreated(pRequest, pResponse);

  if (isLoggingDebug()) {
    if (t != null)
      logDebug("beforeSet created transaction " + t);
    else
      logDebug("beforeSet did not create a transaction.");
  }

  return super.beforeSet(pRequest,pResponse);
}


The handler methods implement the following transactional steps:

   a) Synchronize on the Order object.
    b) Execute logic for modifying the Order object.  For example, the CartModifierFormHandler subclass   has a handleAddItemToOrder method that executes the logic of adding an item to an order.
  c)  Call the OrderManager object’s updateOrder method to save the order data to the repository.
  d) End the synchronization.

The following example shows the handleAddItemToOrder method of the CartModifierFormHandler:



public boolean handleAddItemToOrder (DynamoHttpServletRequest pRequest,
DynamoHttpServletResponse pResponse)
  throws ServletException, IOException
{
  RepeatingRequestMonitor rrm = getRepeatingRequestMonitor();
  String myHandleMethod = "CartModifierOrderFormHandler.handleAddItemToOrder";
  if ((rrm == null) || (rrm.isUniqueRequestEntry(myHandleMethod)))
  {
    Transaction tr = null;
    try {
      tr = ensureTransaction();
      if (getUserLocale() == null) setUserLocale(getUserLocale(pRequest, pResponse));

      //If any form errors found, redirect to error URL:
      if (! checkFormRedirect(null, getAddItemToOrderErrorURL(), pRequest, pResponse))
        return false;

      synchronized(getOrder()) {
        preAddItemToOrder(pRequest, pResponse);

        //If any form errors found, redirect to error URL:
        if (! checkFormRedirect(null, getAddItemToOrderErrorURL(), pRequest, pResponse))
          return false;

        addItemToOrder(pRequest, pResponse);

        //If any form errors found, redirect to error URL:
        if (! checkFormRedirect(null, getAddItemToOrderErrorURL(), pRequest, pResponse))
          return false;

        postAddItemToOrder(pRequest, pResponse);

        updateOrder(getOrder(), MSG_ERROR_UPDATE_ORDER, pRequest, pResponse);
      } // synchronized

      //If NO form errors are found, redirect to the success URL.
      //If form errors are found, redirect to the error URL.
      return checkFormRedirect (getAddItemToOrderSuccessURL(), getAddItemToOrderErrorURL(), pRequest, pResponse);
    }
    finally {
      if (tr != null) commitTransaction(tr);
      if (rrm != null)
        rrm.removeRequestEntry(myHandleMethod);
    }
  }
  else {
    return false;
  }
}

afterSet Method

This method is called once after all form handler processing is completed. It implements the following transactional steps:

    Commit or roll back any transaction that was created in the beforeSet method.  If the transaction was already in place before the beforeSet method was called, the afterSet method does not end the transaction automatically; this is the application’s responsibility.  If a transaction lock was acquired in the beforeSet method, release the lock.
    The following example shows the afterSet method of the PurchaseProcessFormHandler:

public boolean afterSet(DynamoHttpServletRequest pRequest,
DynamoHttpServletResponse pResponse) throws DropletFormException
{
  try
  {
    Transaction t = getCurrentTransaction();

    if (isLoggingDebug()) {
      if (t != null)
        logDebug("afterSet sees currentTransaction as " + t);
      else
        logDebug("afterSet sees no current transaction.");
    }

    // Try to keep the response to this page from being cached.
    ServletUtil.setNoCacheHeaders(pResponse);

    if (t != null && isTransactionCreated(pRequest, pResponse)) {
      if (isLoggingDebug())
        logDebug("afterSet committing transaction " + t);
      commitTransaction(t);
      unsetTransactionCreated(pRequest, pResponse);
    }
  }

Sunday 12 August 2012

Java client for invoking a RESTful service

Below is a sample REST Java client written using Apache Commons HttpClient. Though the same can be achieved using java.net classes HttpClient is preferred as it provides higher and more functional  level of abstraction.



import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;

import atg.core.util.StringUtils;
import atg.nucleus.GenericService;

/**
 * @author kshabarinath
 * This is a sample REST client which is used to access a service exposed using URL pattern
 *
 */


public static void main(String[] args) {
String requestURL = "http://www.thomas-bayer.com/sqlrest/CUSTOMER/999";
HttpClient client = new HttpClient();
GetMethod method = new GetMethod(requestURL);
String line;
InputStream rstream;
try {
int statusCode = client.executeMethod(method);
String response = getResponseBody(method);
System.out.println("RESPONSE::"+response);
} catch(HttpException e){
e.printStackTrace();

}catch (IOException e) {

// TODO Auto-generated catch block
e.printStackTrace();
}finally{
method.releaseConnection();
}
}



private static String getResponseBody(GetMethod method){
if(method!=null && method.hasBeenUsed()){
BufferedReader in = null;
StringWriter stringOut = new StringWriter();
BufferedWriter dumpOut = new BufferedWriter(stringOut,8192);
try{
in=new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream()));
String line = "";
while((line=in.readLine())!=null){
dumpOut.write(line);
dumpOut.newLine();
}
}catch(IOException e){
e.printStackTrace();
}finally{
try{
dumpOut.flush();
dumpOut.close();
if(in!=null){
in.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
return stringOut.toString();
}
return null;
}

OUTPUT of the Program


RESPONSE::<?xml version="1.0"?><CUSTOMER xmlns:xlink="http://www.w3.org/1999/xlink">
    <ID>999</ID>
    <FIRSTNAME>Sylvia</FIRSTNAME>
    <LASTNAME>Ott</LASTNAME>
    <STREET>361 College Av.</STREET>
    <CITY>Viena</CITY>
</CUSTOMER>


Saturday 21 July 2012

Error Codes - Some of the frequently issues




1. 403 - Cannot Serve URLs with character in them


The HTMLServlet and the FileFinderServlet servlets in the request handling servlet pipeline check for the characters '..' and 'null'. If these appear in the URL the "Cannot serve URLs" error message is shown.


These characters are hard coded in the source and cannot be parametrized.


Source Code Snippet: - FileFinderPipelineServlet


 if(pPathInfo.indexOf("..") != -1)
                {
                    sendError(pResponse, 403, "Cannot serve URLs with ..'s in them");
                    return false;
                }
                if(pPathInfo.indexOf('\0') != -1)
                {
                    sendError(pResponse, 403, "Cannot serve URLs with nulls in them");
                    return false;
                }


2. 404 Error Pages Rendered From web.xml Definition Not Multisite Aware 


A 404 error-page was added to the application's web.xml  file
<error-page> 
    <error-code>404</error-code> 
    <location>/error.jsp</location>
</error-page>


However, the request does not appear to be processed by the pipleline, as none of the site formatting or branding for Multisite is being displayed with the 404 error.jsp


The 404 exceptions are handled via the Application Server (ie WebLogic, JBoss, WebShpere). If the web.xml file does not include instructions for how the Applications Servers, Dispatcher should handle ERRORs, the Application Server will not forward the Request to the PageFilter servlet -> DynamoHandler -> SiteContextPipelineServlet , and on down the pipeline. Hence, none of the Formatting/Branding handled by Multisite and the SiteContextPipelineServlet are triggered.


We need to abide to the pattern followed in CRS web.xml when we define the error pages for error codes:


1. Define the filters
<filter>
    <filter-name>PageFilter</filter-name>
    <filter-class>atg.filter.dspjsp.PageFilter</filter-class>
 </filter>
 <filter>
    <filter-name>ErrorFilter</filter-name>
    <filter-class>atg.servlet.ErrorFilter</filter-class>
  </filter>


2. Define the filter mappings
 <filter-mapping>
    <filter-name>ErrorFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>ERROR</dispatcher>
  </filter-mapping>
  <filter-mapping>
    <filter-name>PageFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>ERROR</dispatcher>
    <dispatcher>FORWARD</dispatcher>
  </filter-mapping>


3. Include all the error page elements
 <error-page>
    <error-code>404</error-code>
    <location>/global/pageNotFound.jsp</location>
  </error-page>
  <error-page>
    <error-code>500</error-code>
    <location>/global/serverError.jsp</location>
  </error-page>

Thursday 19 July 2012

AccessControlServlet and Profile's SecurityStatus


We normally use Profile.transient property to determine if a user is loggedIn. The method is not secure as users that have been logged in automatically have transient property set to false. You should check the profile's security status before displaying secured data such as user's profile or credit card information.


The securityStatus property of the Profile component is used to indicate the type of verification the user has passed through. When a user logs in (or is automatically logged in), the Personalization module sets the value of this property to an integer that indicates the login method used. This can be used to have a tiered login structure where a user can be automatically logged in but they must enter in their login credentials before being able to view certain information such as their address, order history, or credit card information.


The securityStatus property is set according to this table: 
  


Value

Login Method Used

0

Anonymous login

1

Auto-login by URL parameter

2

Auto-login by cookie

3

Login by http-(basic-authentication)

4

Explicit login or registration in the Personalization module via form handler

5

Explicit login or registration in the Personalization module via form handler underhttps protocol.

6

Certificate (not supported by ATG at this time).


AccessControlServlet 

This servlet checks the requestURI to see if it matches any of the requestedURLs identified in it's accessController map. The accessControllermap is made up of URLs matched to an AccessController instance that governs the rules to determine when a URL is requested whether the active Profile is permitted to view the page. When access is denied by an AccessController it calls the AccessControllerServlet which 
redirects the user to the URL in the deniedAccessURL.


Example of instance of AccessController that is invoked when the URL requested is present in the accessController map. allowAccess method uses the Profile's security Status



public boolean allowAccess(Profile pProfile, DynamoHttpServletRequest pRequest){
boolean isLoggedIn = false;
try {
Integer securityStatus = getProfileTools().getSecurityStatus(pProfile);
if(securityStatus >= getProfileTools().getPropertyManager().getSecurityStatusLogin()){
isLoggedIn = true;
}
} catch (PropertyNotFoundException propExc) {
if(isLoggingError()){
logError("PropertyNotFoundException", propExc);
}
}
return isLoggedIn;
}

public String getDeniedAccessURL(Profile pProfile){
DynamoHttpServletRequest dynamoRequest = ServletUtil.getCurrentRequest();
String requestURI = ServletUtil.getCurrentRequestURI(dynamoRequest);
StringBuffer requestURL = new StringBuffer();
if(!StringUtils.isBlank(requestURI)){
requestURL.append(requestURI);
}
String queryString = dynamoRequest.getQueryString();
if(!StringUtils.isBlank(queryString)){
StringBuffer modifiedQueryString = new StringBuffer();
String[] queryStringTokens = StringUtils.splitStringAtString(queryString, "&");
if(queryStringTokens!=null && queryStringTokens.length > 0){
StringTokenizer queryParamAndValue = null;
String paramName = "";
int maxCount = queryStringTokens.length;
for(int i=0;i<maxCount;i++){
queryParamAndValue = new StringTokenizer(queryStringTokens[i],"=");
if(queryParamAndValue.hasMoreTokens()){
paramName = queryParamAndValue.nextToken();
}
if(!"_Dargs".equalsIgnoreCase(paramName)){
if(i<maxCount){
modifiedQueryString.append(queryStringTokens[i]+"&");
}else{
modifiedQueryString.append(queryStringTokens[i]);
}
}
}
}
queryString = modifiedQueryString.toString();
requestURI = requestURL.append("&").append(queryString).toString();
}
return requestURI;
}


Updating Orders in Commerce Code

When you are using the Order object with synchronization and transactions, there is a specific usage pattern that is critical to follow. Not following the expected pattern can lead to unnecessary ConcurrentUpdateExceptions, InvalidVersionExceptions and deadlocks. The following sequence must be strictly adhered to:

1. Obtain local lock on profile id
2. Begin transaction
3.Synchronize on order
4.Perform all modifications to the order object
5.Call OrderManager.updateOrder
6.End Synchronization
7.End Transaction
8.Release local lock on profile id

Steps 1,2,7 and 8 are done for you in beforeSet and afterSet methods for ATG form handlers where order updates are expected. These include formhandlers that extend PurchaseProcessFormHandler.

If you write a custom code updating an order outside of PurchaseProcessFormHandler (Eg: coupon. droplet, pipeline servlet) etc you code should acquire the lock before updating the order.

The following code snippet is an example to load cookied order and depicts the pattern that is useful to prevent ConcurrentUpdateExceptions, InvalidVersionExceptions and dead locks when multiple threads attempt to update the same order on the same ATG instance.



public void service(DynamoHttpServletRequest request,
DynamoHttpServletResponse response) throws IOException,
ServletException {

ClientLockManager lockManager = getLocalLockManager(); // Should be configured as /atg/commerce/order/LocalLockManager
Profile profile = (Profile)request.resolveName("/atg/userprofiling/Profile");
boolean acquireLock = false;
try
{
 acquireLock = !lockManager.hasWriteLock( profile.getRepositoryId(), Thread.currentThread() );
 if ( acquireLock )
 lockManager.acquireWriteLock( profile.getRepositoryId(), Thread.currentThread() );
 TransactionDemarcation td = new TransactionDemarcation();
 td.begin( getTransactionManager(),TransactionDemarcation.REQUIRES_NEW );
 boolean shouldRollback = false;
 try
 {
 String cookieOrderId =getCookieManager().getCookie("orderIdCookie",request);
Order order = (Order)getOrderManager().loadOrder(cookieOrderId);
OrderHolder orderHolder = (OrderHolder)request.resolveName("/atg/commerce/ShoppingCart");
synchronized (order) {
orderHolder.setCurrent(order);
order.setProfileId(profile.getRepositoryId());
CommerceProfileTools profileTools = getProfileTools();
if(order.getCommerceItemCount() > 0){
profileTools.repriceShoppingCarts(profile,orderHolder,profileTools.getRepriceOrderPricingOp(),request,response);
}
getOrderManager().updateOrder(order);
}
 }
 catch (CommerceException commException) {
shouldRollback = true;
if(isLoggingError()){
logError("CommerceException which loading order", commException);
}
}
 finally
 {
   try
   {
     td.end( shouldRollback );
   }
   catch ( Throwable th )
   {
     logError( th );
   }
 }
} catch (DeadlockException deadLockExc) {
if(isLoggingError()){
logError("DeadLockException", deadLockExc);
}
} catch (TransactionDemarcationException transDemExc) {
if(isLoggingError()){
logError("TransactionDemarcationException", transDemExc);
}
}
finally
{
 try
 {
   if ( acquireLock )
     lockManager.releaseWriteLock( profile.getRepositoryId(), Thread.currentThread(), true );
 }
 catch( Throwable th )
 {
   logError( th );
 }
}
}

Thursday 12 July 2012

WHAT'S NEW IN ATG 10.1


We have been waiting for 10.1 to released to see the changes Oracle would bring about in ATG Products Suite and here it is


WHAT'S NEW IN ATG 10.1

  • ATG Platform-Localization of Business Tools
  • ATG Platform-External Caching using Coherence 
  • ATG Commerce-B2B with B2C Commerce Merge
  •  ATG Merchandising -Visual Merchandising
  •  ATG Merchandising-Preview By Date and Time 
  •  ATG Merchadinsing-Workbench
  •  ATG Merchadinsing-Multi‐edit Using Workbench
  • ATG Promotions-Exclusion and Stacking rules 
  • ATG Promotions-Coupon Tracking
  • ATG Promotions-Serialised Coupons
  • ATG Promotions-Gift With Purchase 
  • ATG CRS-Mobile Refernce Applications 
  • ATG CRS-Fluoroscope (Allows developmers to examine which elements of the page relate to which JSP files, the  stack of included files, and parameters being used.)
  • ATG CRS-Gift with Purchase Promotions
  • ATG CRS-Integration with ATG Recommendations
  • ATG CRS-Recently Viewed Items
  • ATG Search-Now for legacy customers only, Endeca recommended for new customers.
  • ATG Commerce Business Intelligence, Oracle BI (Enterprise Edition) replaces Cognos.
  • ATG Campaign Optimizer and Outreach, for legacy customers only.
  • ATG Knowledge and Self Service, for legacy customers only. 

Sunday 8 July 2012

Changing the default Big String Editor in Merchandising - Oracle ATG 10

The default big string editor creates many problems such as simple ' in Merchandising transforms to &apos on the store front and in the assets tab which loads the HTML content in the EditLive! editor. 

The above stated issue which is because of the Flex html editor can be fixed by following the below steps

(1) Load up the ACC and connect it to your CA/Merch server.

(2) When the ACC has loaded, navigate to the Publishing > View Mapping tab.

(3) The next step is to find the text editors available to the AssetManager. Do this by performing the following search in the ACC:
(a) List Items of type propertyView whose name contains "big string"
You will see a list of 4 items when you issue this query. The relevant ones are named "AA AssetManager plain text big string propertyView" and "AssetManager big string propertyView"

(b) If you select "AA AssetManager plain text big string propertyView" you should see that it has the property isDefault set to false.
You need to set this to true and then save the change.

(c) You then need to select "AssetManager big string propertyView"and set isDefault to false.
Save this change.

(4) Now open up the BCC and create a new Merchandising project.

(5) Within this project, create or edit an asset that has a property of type 'big string' and add some HTML content.
     You should now see that the HTML tags are saved correctly by the plain text editor

Customizing Merchandising UI - Oracle ATG 10

As we are all aware Merchandising UI in ATG 10 has been delivered using Flex UI


Below are the steps that need to be followed to made to edit Ids while creating product asset withing Flex


1. Go to Dynamo Administration of BCC
2. Navigate to /atg/web/viewmapping/ViewMappingRepository component
3. Run this XML Operation Tags on the Repository:
<print-item item-descriptor="itemMapping" id="AmImPrdt" />



------ Printing item with id: AmImPrdt
<add-item item-descriptor="itemMapping" id="AmImPrdt">
 <set-property name="itemName"><![CDATA[product]]></set-property>
 <set-property name="description"><![CDATA[AssetManager product itemMapping]]></set-property>
 <set-property name="attributes"><![CDATA[resourceBundle=AmAvPrdtBnd]]></set-property>
 <set-property name="mode"><![CDATA[AmMmDef]]></set-property>
 <set-property name="viewMappings"><![CDATA[AmIvmPrdtGen,AmIvmPrdtSkus,AmIvmPrdtCat,AmIvmPrdtMedia,AmIvmPrdtPrice,AmIvmPrdtCrossEdit,AmIvmPrdtAdv]]></set-property>
 <set-property name="formHandler"><![CDATA[AmFhDef]]></set-property>
 <set-property name="itemPath"><![CDATA[/atg/commerce/catalog/ProductCatalog]]></set-property>
 <set-property name="name"><![CDATA[AssetManager]]></set-property>
</add-item>

4. Run this XML Operation Tags on the Repository:
<add-item item-descriptor="attributeValue" id="AmAvPrdtCreateId">
 <set-property name="value"><![CDATA[true]]></set-property>
</add-item>



5. Append newly created attributeValue into attributes property of AmImPrdt itemMapping using this XML Operation Tags on the Repository:
IMPORTANT NOTE: There shouldn't be any carriage return (ENTER) or white space signs inside ![CDATA[..]]
<update-item item-descriptor="itemMapping" id="AmImPrdt">
 <set-property name="attributes"><![CDATA[resourceBundle=AmAvPrdtBnd,showCreationId=AmAvPrdtCreateId]]></set-property>
</update-item>