Известно, что для обновления заказа в ATG Form-Handlers
, который не наследуется от PurchaseProcessFormHanlder, необходимо использовать следующий шаблон:
boolean acquireLock = false;
ClientLockManager lockManager = getLocalLockManager();
try {
acquireLock = !lockManager.hasWriteLock(profile.getRepositoryId(), Thread.currentThread());
if (acquireLock) {
lockManager.acquireWriteLock(profile.getRepositoryId(), Thread.currentThread());
}
boolean shouldRollback = false;
TransactionDemarcation transactionDemarcation = new TransactionDemarcation();
TransactionManager transactionManager = getTransactionManager();
transactionDemarcation.begin(transactionManager, TransactionDemarcation.REQUIRED);
try {
synchronized (getOrder()) {
...
...
...
}
} catch (final Exception ex) {
shouldRollback = true;
vlogError(ce, "There has been an exception during processing of order: {0}", getOrder().getId());
} finally {
try {
transactionDemarcation.end(shouldRollback);
} catch (final TransactionDemarcationException tde) {
vlogError(tde, "TransactionDemarcationException during finally: {0}", tde.getMessage());
} finally {
vlogDebug("Ending Transaction for orderId: {0}", order.getId());
}
}
} catch (final DeadlockException de) {
vlogError(de, "There has been an exception during processing of order: {0}", order.getId());
} catch (final TransactionDemarcationException tde) {
vlogError(tde, "There has been an exception during processing of order: {0}", order.getId());
} finally {
try {
if (acquireLock) {
lockManager.releaseWriteLock(getOrder().getProfileId(), Thread.currentThread(), true);
}
} catch (final Throwable th) {
vlogError(th, "There has been an error during release of write lock: {0}", th.getMessage());
}
}
Теоретически любой FormHandler
, который наследуется от PurchaseProcessFormHandler, уже реализует следующие шаги OOTB:
Приобретите LocalLockManager, чтобы параллельные потоки не изменяли один и тот же порядок:
try { acquireLock = !lockManager.hasWriteLock(profile.getRepositoryId(), Thread.currentThread()); if (acquireLock) { lockManager.acquireWriteLock(profile.getRepositoryId(), Thread.currentThread()); } } catch (final DeadlockException de) { vlogError(de, "There has been an exception during processing of order: {0}", order.getId()); }
Создайте новую транзакцию:
try { TransactionDemarcation transactionDemarcation = new TransactionDemarcation(); TransactionManager transactionManager = getTransactionManager(); transactionDemarcation.begin(transactionManager, TransactionDemarcation.REQUIRED); } catch (final TransactionDemarcationException tde) { vlogError(tde, "There has been an exception during processing of order: {0}", order.getId()); }
Используется Завершение транзакции:
try { TransactionManager transactionManager = getTransactionManager(); Transaction transaction = transactionManager.getTransaction(); // If transaction is elegible for commiting: transactionManager.commit(); transaction.commit(); // otherwise transactionManager.rollback(); transaction.rollback(); } catch (final Exception ex) { error = true; vlogError(ex, "There has been an exception during processing of order: {0}", order.getId()); } finally { // handle the error }
Снимите блокировку, используемую для транзакции:
finally { ClientLockManager lockManager = getLocalLockManager(); lockManager.releaseWriteLock(profile.getRepositoryId(), Thread.currentThread(), true); }
Согласно документации ATG, следующие методы реализуют поведение, описанное выше:
Метод: перед набором
Вызывается перед установкой любых методов setX в этой форме при отправке формы, которая изменяет свойства этого обработчика формы. При необходимости создает транзакцию в начале процесса отправки формы, дополнительно получая локальную блокировку, чтобы предотвратить создание транзакций несколькими формами, которые могут изменить один и тот же порядок.
- Steps: 1 & 2
Метод: afterSet
Вызывается после установки любых методов setX в этой форме при отправке формы, которая изменяет свойства этого обработчика формы. Фиксирует или откатывает любую транзакцию, созданную в beforeSet, и освобождает любую блокировку, полученную в это время.
- Steps: 3 & 4
Например, вам нужно будет выполнить следующие процедуры, чтобы обновить заказ:
Синхронизируйте блок кода, который будет использоваться для
order updating
, чтобы избежать параллелизма потоков.synchronized (getOrder()) { ... ... ... }
Выполните изменения заказа:
synchronized (getOrder()) { getOrder().setXXX(); getOrder().removeXXX(); }
Обновите порядок (будет запущена цепочка конвейера
updateOrder
):synchronized (getOrder()) { ... ... ... getOrderManager().updateOrder(order); }
Это довольно просто, если вам не нужно редактировать заказ в любом из следующих сценариев:
- Обработчики форм или настраиваемые обработчики форм, не входящие в иерархию
PurchaseProcessFormHandler
. - Классы помощников или инструментов.
- Процессоры
- Веб-службы ATG REST
- и т. д.
Если это так, вам придется внедрить шаблон транзакций в свои компоненты.
Вопросы!
- Есть ли какой-либо другой шаблон, который можно использовать вместо шаблона транзакций?
- Можно ли реализовать/переопределить методы
beforeSet
иafterSet
в FormHandlers точно так же, как ATG делает это вPurchaseProcessFormHandler
- Известны ли вам какие-либо другие подходы?