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!!

2 comments: