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);
}
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);
}
}
No comments:
Post a Comment