An important concept in the message processing in the fulfillment process is that no component should handle more than one message per order at any given time.
For example, a component is currently handling a ModifyOrderNotification
message for orderId
‘1234’ and a ModifyOrder
message for orderId
‘1234’ is received during processing. The ModifyOrder
message blocks and waits until the first message finishes running. This does not prohibit any messages that come for another orderId
from being processed. This is accomplished with locking and the ClientLockManager
. All fulfillment components use the lock manager located at /atg/dynamo/service/ClientLockManager
.
The lock acquired is for the key that is returned by the method getKeyForMessage
in OrderFulfiller
and HardgoodFulfiller
. The default implementation returns the orderId
specified in the message. This method can be overridden if the key to determine the locking needs to be changed but you want to preserve the principle of having one message per key/message at a time.
The design of the OrderFulfiller
and the HardgoodFulfiller
uses the ClientLockManager
to prevent one component from processing messages for two different orders at the same time. Extending the ModificationHander
for either class does not require any locking changes. The only time you should be concerned with locking is if the HardgoodFulfiller
is not extended when a fulfiller class is created.
Note: Every ClientLockManager
(one per ATG Commerce instance) should be configured to point to the ATG Commerce instance running the ServerLockManager
. Every ATG Commerce component should use the same ClientLockManager
. For more information on the ServerLockManager
, see the SQL Repository Caching chapter in the ATG Repository Guide.
The following example demonstrates how the lock manager is used:
TransactionDemarcation td = new TransactionDemarcation(); try { td.begin(getTransactionManager(), td.REQUIRED); getClientLockManager().acquireWriteLock(pOrderId); LockReleaser lr = new LockReleaser(getClientLockManager(), getTransactionManager().getTransaction()); lr.addWriteLock(pOrderId); <insert your code here> catch (DeadlockException de) { if(isLoggingError()) logError(de); return false; } catch (LockManagerException lme) { if(isLoggingError()) logError(lme); return false; } catch(TransactionDemarcationException t) { if(isLoggingError()) logError(t); return false; } finally { try { td.end(); } catch(TransactionDemarcationException tde) { if(isLoggingError()) logError(tde); } }
The LockReleaser
registers each lock with the transaction. The lock is released when the transaction ends. Because of this, it is imperative that a transaction be in place when the LockReleaser
is created. This is the reason for all the code using TransactionDemarcation
. For more information on transactions, see the Transaction Management chapter in the ATG Programming Guide.