Thursday, February 14, 2013

How can improper coding leads to InvalidVersionException?

Small example showing how "atg.commerce.order.InvalidVersionException" is thrown when coding is done improperly.
In below example: one of the order property is udpated and updateOrder is called. In below case InvalidVersionException is thrown.
Note: Please go through http://atgkid.blogspot.com/2011/11/regarding-concurrentupdateexception-and.html
to know more about InvalidVersionException and ConcurrentUpdateException.

public class TestOrderDroplet extends DynamoServlet {
    @Override
    public void service(DynamoHttpServletRequest req,
            DynamoHttpServletResponse res) throws ServletException, IOException {
        OrderHolder ordholder = (OrderHolder) req.resolveName("/atg/commerce/ShoppingCart");
        OrderManager ordMgr = (OrderManager) req.resolveName("/atg/commerce/order/OrderManager");
        WalgreensOrderImpl order = (WalgreensOrderImpl) ordholder.getCurrent();

        // a custom persistent property called storeNumber at order level
        order.setStoreNumber(59151);
       
        try {
        ordMgr.updateOrder(order);
    } catch (CommerceException e) {
        e.printStackTrace();
    }
    }
}
In jsp - test.jsp - Just calling above droplet
<%@ taglib uri="dsp" prefix="dsp"%>
<dsp:page>
    <dsp:droplet name="/com/TestOrderDroplet">
    </dsp:droplet>
</dsp:page>
When above page is accessed

In logs -
atg.commerce.order.InvalidVersionException: This order (2368070835) is out of date. Changes have been made to the order and the operation should be resubmitted.
Order version 15, Repository item version 16.
        at atg.commerce.order.OrderManager.updateOrder(OrderManager.java:2670)
        at com.TestOrderDroplet.service(TestOrderDroplet.java:26)
        at atg.servlet.DynamoServlet.service(DynamoServlet.java:123)
        at atg.taglib.dspjsp.DropletTag.invokeServlet(DropletTag.java:349)
        at atg.taglib.dspjsp.DropletTag.doAfterBody(DropletTag.java:616)
        at org.apache.jsp.test_jsp._jspx_meth_dsp_005fdroplet_005f0(test_jsp.java:138)
        at org.apache.jsp.test_jsp._jspx_meth_dsp_005fpage_005f0(test_jsp.java:98)
        at org.apache.jsp.test_jsp._jspService(test_jsp.java:67)

Why above exception is thrown?
During "order.setStoreNumber(59151)" value is updated in order table, updateItem is called and order item version is updated (but Order (session) object does'nt know about version change)
During "updateOrder" - ATG OOTB will compare Order (session) object version with order item version, since both are not equal - InvalidVersionException is thrown.

How can we avoid such type of exception?
By having a transaction while update order. Go through below example.
public class TestOrderDroplet extends DynamoServlet {
  
    @Override
    public void service(DynamoHttpServletRequest req,
            DynamoHttpServletResponse res) throws ServletException, IOException {
        OrderHolder ordholder = (OrderHolder) req.resolveName("/atg/commerce/ShoppingCart");
        OrderManager ordMgr = (OrderManager) req.resolveName("/atg/commerce/order/OrderManager");
        MutableRepository repo = (MutableRepository) req.resolveName("/atg/commerce/order/OrderRepository");
        TransactionManager tManger = (TransactionManager) req.resolveName("/atg/dynamo/transaction/TransactionManager");

        WalgreensOrderImpl order = (WalgreensOrderImpl) ordholder.getCurrent();
      
        // added for transaction
        TransactionDemarcation td =new TransactionDemarcation();
        boolean rollback = false;
      
        try {
            // transaction beginning
            td.begin(tManger,TransactionDemarcation.REQUIRES_NEW);
          
            RepositoryItem orderItem = repo.getItemForUpdate(order.getId(), "order");
          
            logDebug("Before set - order version : "+order.getVersion());
            logDebug("Before set - order item version: "+orderItem.getPropertyValue("version"));
          
            order.setStoreNumber(59151);

            // Note: if you call updateItem on order item, order item version will be increased
            // but order (session) object is not, so if you call udpateItem here
            // during update order, InvalidVersionException is thrown even with transaction in place.
            // repo.updateItem((MutableRepositoryItem)orderItem);
          
            logDebug("After set - order version : "+order.getVersion());
            logDebug("After set order item version: "+orderItem.getPropertyValue("version"));

           ordMgr.updateOrder(order);
      
            logDebug("After updateOrder - order version : "+order.getVersion());
            logDebug("After updateOrder order item version: "+orderItem.getPropertyValue("version"));

    } catch (Exception e) {
        e.printStackTrace();
        rollback = true;
    }
      
    try {
        // transaction end
        td.end(rollback);
    } catch (TransactionDemarcationException e) {
        e.printStackTrace();
    }
  
    }

}
In logs -
**** debug      Thu Feb 14 11:17:38 CST 2013    1360862258581   /com/TestOrderDroplet   Before set - order version : 28
**** debug      Thu Feb 14 11:17:39 CST 2013    1360862259957   /com/TestOrderDroplet   Before set - order item version: 28
**** debug      Thu Feb 14 11:17:42 CST 2013    1360862262224   /com/TestOrderDroplet   After set - order version : 28
**** debug      Thu Feb 14 11:17:43 CST 2013    1360862263115   /com/TestOrderDroplet   After set order item version: 28
**** debug      Thu Feb 14 11:17:44 CST 2013    1360862264475   /com/TestOrderDroplet   After updateOrder - order version : 30
**** debug      Thu Feb 14 11:17:45 CST 2013    1360862265225   /com/TestOrderDroplet   After updateOrder order item version: 30

Why above code will work without "InvalidVersionException" exception?

During "order.setStoreNumber(59151)" since transaction is in place, value is NOT updated yet in database or in item cache, so item version is NOT updated.
During "updateOrder" - ATG OOTB will compare Order (session) object version with order item version, in this case both are equal (since both are not updated yet).
ATG OOTB pipeline "updateOrder" is executed and ProcSaveOrderObject will call updateItem on orderItem, which will update the item version.
once whole pipeline is executed at the end of the updateOrder method - order (session) object is updated with order Item version.So once updateOrder is finished both order (session) object version and order item version will be in sync.

Hope this helps!!

Wednesday, February 13, 2013

Is calling updateItem necessary after calling setPropertyValue on item?

In this article, below scenarios are considered to see how values are updated in database and at what point.
Case 1 : What happens if setPropertyValue is called on an item - without calling updateItem and without any parent transaction?
Case 2 : What happens if setPropertyValue is called on an item - calling updateItem and without any parent transaction?
Case 3 : What happens if setPropertyValue is called on item - with parent transaction and without updateItem call?
Case 4 : What happens if setPropertyValue is called on item - With Transaction, with UpdateItem call?

=============================================================================================
Case 1 : What happens if setPropertyValue is called on an item - without calling updateItem and without any parent transaction?
for every item.setPropertyValue - update sql query is fired, commit is called, value is updated in database.

For example -
MutableRepositoryItem mutItem = repo.getItemForUpdate("900001", "addressValidation_req_log");
mutItem.setPropertyValue("city", "Des Plaines2");
In logs -
 UPDATE www_addr_verify_req_log
    SET CITY=?
  WHERE REQUEST_LOG_ID=?
-- Parameters --
p[1] = {pd: city} Des Plaines2 (java.lang.String)
p[2] = {pd} 900001 (java.lang.String)

updateItem(addressValidation_req_log:900001) (changes applied: {city=Des Plaines2})
=============================================================================================
Case 2 : What happens if setPropertyValue is called on an item - calling updateItem and without any parent transaction?
Even though updateItem is called it does'nt mean that changes in database will get updated only after updateItem is called.
In this case too, for every item.setPropertyValue - update sql query is fired, commit is called, value is updated in database.
Note: When updateItem is called seperately - Since no changes nothing is done.

For example -
mutItem.setPropertyValue("city", "Des Plaines2");
mutItem.setPropertyValue("streetAddr2", "Des 2");
repo.updateItem(mutItem);

MutableRepositoryItem mutItem = repo.getItemForUpdate("900001", "addressValidation_req_log");
mutItem.setPropertyValue("city", "Des Plaines2");
In logs -
 UPDATE www_addr_verify_req_log
    SET CITY=?
  WHERE REQUEST_LOG_ID=?
-- Parameters --
p[1] = {pd: city} Des Plaines2 (java.lang.String)
p[2] = {pd} 900001 (java.lang.String)

updateItem(addressValidation_req_log:900001) (changes applied: {city=Des Plaines2})

 UPDATE www_addr_verify_req_log
    SET STREET_ADDR2=?
  WHERE REQUEST_LOG_ID=?
-- Parameters --
p[1] = {pd: streetAddr2} Des 2 (java.lang.String)
p[2] = {pd} 900001 (java.lang.String)

updateItem(addressValidation_req_log:900001) (changes applied: {streetAddr2=Des 2})

updateItem(addressValidation_req_log:900001) (no changes)
=============================================================================================
Case 3 : What happens if setPropertyValue is called on item - with parent transaction and without updateItem call?
If transaction is in place, then only one update query is fired for all property changes for that item.
A single update query is fired at for EACH item (even if there are many properties changed) at the end of transaction.

For example:
MutableRepositoryItem mutItem = repo.getItemForUpdate("900001", "addressValidation_req_log");
mutItem.setPropertyValue("city", "Des Plaines2");
mutItem.setPropertyValue("streetAddr2", "Des 2");

setPropertyValue(addressValidation_req_log:900001.city,Des Plaines2)
setPropertyValue(addressValidation_req_log:900001.streetAddr2,Des 2)

MutableRepositoryItem mutItem1 = repo1.getItemForUpdate("1335", "categorytier3info");
mutItem1.setPropertyValue("createUser", "mktg-team1");
mutItem1.setPropertyValue("updateUser", "mktg-team1");

setPropertyValue(categorytier3info:1335.createUser,mktg-team1)
setPropertyValue(categorytier3info:1335.updateUser,mktg-team1)

td.end(true);
In logs -
 UPDATE www_addr_verify_req_log
    SET STREET_ADDR2=?,CITY=?
  WHERE REQUEST_LOG_ID=?
-- Parameters --
p[1] = {pd: streetAddr2} Des 2 (java.lang.String)
p[2] = {pd: city} Des Plaines2 (java.lang.String)
p[3] = {pd} 900001 (java.lang.String)

updateItem(addressValidation_req_log:900001) (changes applied: {streetAddr2=Des 2,city=Des Plaines2})
updateItem(categorytier3info:1335) (changes applied: {createUser=mktg-team1,updateUser=mktg-team1})

 UPDATE WWW_CONTACTUS_DTL
    SET CREATE_USER=?,UPDATE_USER=?
  WHERE SUB_CATEGORY_ID=?
-- Parameters --
p[1] = {pd: createUser} mktg-team1 (java.lang.String)
p[2] = {pd: updateUser} mktg-team1 (java.lang.String)
p[3] = {pd} 1335 (java.lang.Integer)

=============================================================================================
Case 4: What happens if setPropertyValue is called on item - With Transaction, with UpdateItem?
update query is fired for each item when updateItem is called, but changes got updated in database only
when transaction is commited.

Note: The difference between case 3 and case 4 is, in case 3 all update queries are fired and committed when transaction ends. In case 4 update queries are fired during updateItem call and commit will be done only at the end of the transaction.

For example:
MutableRepositoryItem mutItem = repo.getItemForUpdate("900001", "addressValidation_req_log");
mutItem.setPropertyValue("city", "Des Plaines2");
mutItem.setPropertyValue("streetAddr2", "Des 2");
repo.updateItem(mutItem);

MutableRepositoryItem mutItem1 = repo1.getItemForUpdate("1335", "categorytier3info");
mutItem1.setPropertyValue("createUser", "mktg-team1");
mutItem1.setPropertyValue("updateUser", "mktg-team1");
repo1.updateItem(mutItem1);
In logs -
setPropertyValue(addressValidation_req_log:900001.city,Des Plaines2)
setPropertyValue(addressValidation_req_log:900001.streetAddr2,Des 2)
 UPDATE www_addr_verify_req_log
    SET STREET_ADDR2=?,CITY=?
  WHERE REQUEST_LOG_ID=?
-- Parameters --
p[1] = {pd: streetAddr2} Des 2 (java.lang.String)
p[2] = {pd: city} Des Plaines2 (java.lang.String)
p[3] = {pd} 900001 (java.lang.String)
updateItem(addressValidation_req_log:900001) (changes applied: {streetAddr2=Des 2,city=Des Plaines2})

setPropertyValue(categorytier3info:1335.createUser,mktg-team1)
setPropertyPD{categorytier3info,1335,updateUser} = mktg-team1 (java.lang.String)
 UPDATE WWW_CONTACTUS_DTL
    SET CREATE_USER=?,UPDATE_USER=?
  WHERE SUB_CATEGORY_ID=?
-- Parameters --
p[1] = {pd: createUser} mktg-team1 (java.lang.String)
p[2] = {pd: updateUser} mktg-team1 (java.lang.String)
p[3] = {pd} 1335 (java.lang.Integer)
updateItem(categorytier3info:1335) (changes applied: {createUser=mktg-team1,updateUser=mktg-team1})
=============================================================================================
Hope this helps!!