If you ever update an order outside of a transaction then the next time you update it within a transaction you will get the infamous, dreaded InvalidVersionException.
WARN atg.commerce.order.ShoppingCartModifier
atg.commerce.order.InvalidVersionException: This order (o3830002) is out of date. Changes have been made to the order and the operation should be resubmitted. Order version 333, Repository item version 334.
at atg.commerce.order.OrderManager.updateOrder(OrderManager.java:2557)
For example this could happen if you update your order in your JSP page.
<dsp:setvalue bean=â€order.foo†value=â€bar†/>
To fix this problem you must always make sure to update an order within a transaction like this.
Transaction tr = null;
try {
tr = ensureTransaction();
synchronized (getOrder()) {
getOrder().setFoo("bar");
try {
getOrderManager().updateOrder(order);
}
catch (Exception exc) {
processException(exc, MSG_ERROR_UPDATE_ORDER, pRequest, pResponse);
}
}
}
finally {
if (tr != null) commitTransaction(tr);
}
In some cases you might find a method is called within a transaction by another method and in other cases it is not.
public boolean handleFoo(DynamoHttpServletRequest req, DynamoHttpServletResponse res) {
Transaction tr = null;
try {
tr = ensureTransaction();
synchronized (getOrder()) {
setFoo("bar");
try {
getOrderManager().updateOrder(order);
}
catch (Exception exc) {
processException(exc, MSG_ERROR_UPDATE_ORDER, pRequest, pResponse);
}
}
return checkFormRedirect(getSuccessUrl(), getErrorUrl(), req, res);
}
finally {
if (tr != null) commitTransaction(tr);
}
}
public void setFoo(String foo) {
getOrder().setFoo(foo);
}
In the above example the handleFoo method properly updates the order within the transaction. However calling the setFoo method directly will cause a problem since the order is not updated within a transaction. To fix this you use the same pattern again to ensure the order is updated within a transaction. It is okay to do this more than once during a request.
To debug this problem you can use Eclipse to make sure that wherever you update an order is always within a transaction. You can use the debugger to find which methods are called and/or you can use the call hierarchy to find out how methods are called.
Another way to help debug this is adding JSP code similar to the one I list below. It outputs the version of the order that the form handler has and the version that is in the repository. If there is a difference then you know that the action you took before at some point updated the order outside of a transaction.
<dspel:getvalueof bean="ShoppingCartModifier.order.id" var="orderId" />
FORM HANDLER ORDER ID: ${orderId}<br/>
FORM HANDLER ORDER VERSION: <dsp:valueof bean="ShoppingCartModifier.order.version" /><br/>
<dsp:droplet name="/atg/dynamo/droplet/RQLQueryForEach">
<dsp:param name="queryRQL" value="ID IN { \"${orderId}\" }"/>
<dsp:param name="repository" value="/atg/commerce/order/OrderRepository"/>
<dsp:param name="itemDescriptor" value="order"/>
<dsp:oparam name="output">
REPOSITORY ORDER VERSION: <dsp:valueof param="element.version"/><br/>
</dsp:oparam>
</dsp:droplet>
For further reading please see Nabble – ATG Dynamo – Commerce Assist Returns and the Transaction Management section in the ATG Programming Guide.