ATG: Шаблон заказа обновления

Известно, что для обновления заказа в 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:

  1. Приобретите 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());
    }
    
  2. Создайте новую транзакцию:

    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());
    }
    
  3. Используется Завершение транзакции:

    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
    }
    
  4. Снимите блокировку, используемую для транзакции:

    finally {
        ClientLockManager lockManager = getLocalLockManager();
        lockManager.releaseWriteLock(profile.getRepositoryId(), Thread.currentThread(), true);
    }
    

Согласно документации ATG, следующие методы реализуют поведение, описанное выше:

  1. Метод: перед набором

    Вызывается перед установкой любых методов setX в этой форме при отправке формы, которая изменяет свойства этого обработчика формы. При необходимости создает транзакцию в начале процесса отправки формы, дополнительно получая локальную блокировку, чтобы предотвратить создание транзакций несколькими формами, которые могут изменить один и тот же порядок.

    • Steps: 1 & 2
  2. Метод: afterSet

    Вызывается после установки любых методов setX в этой форме при отправке формы, которая изменяет свойства этого обработчика формы. Фиксирует или откатывает любую транзакцию, созданную в beforeSet, и освобождает любую блокировку, полученную в это время.

    • Steps: 3 & 4

Например, вам нужно будет выполнить следующие процедуры, чтобы обновить заказ:

  1. Синхронизируйте блок кода, который будет использоваться для order updating, чтобы избежать параллелизма потоков.

    synchronized (getOrder()) {
        ...
        ...
        ...
    }
    
  2. Выполните изменения заказа:

    synchronized (getOrder()) {
        getOrder().setXXX();
        getOrder().removeXXX();
    }
    
  3. Обновите порядок (будет запущена цепочка конвейера updateOrder):

    synchronized (getOrder()) {
        ...
        ...
        ...
        getOrderManager().updateOrder(order);
    }
    

Это довольно просто, если вам не нужно редактировать заказ в любом из следующих сценариев:

  • Обработчики форм или настраиваемые обработчики форм, не входящие в иерархию PurchaseProcessFormHandler.
  • Классы помощников или инструментов.
  • Процессоры
  • Веб-службы ATG REST
  • и т. д.

Если это так, вам придется внедрить шаблон транзакций в свои компоненты.

Вопросы!

  • Есть ли какой-либо другой шаблон, который можно использовать вместо шаблона транзакций?
  • Можно ли реализовать/переопределить методы beforeSet и afterSet в FormHandlers точно так же, как ATG делает это в PurchaseProcessFormHandler
  • Известны ли вам какие-либо другие подходы?

person Eder    schedule 01.06.2016    source источник
comment
Ваш код выше, похоже, переключается между получением блокировки записи для заказа и профиля. Оракул рекомендует установить блокировку профиля. Более подробную информацию можно найти на странице support.oracle.com/epmos/faces/DocumentDisplay? id=1362812.1. Почему вы хотите следовать какой-либо другой схеме, отличной от той, которая предписана поставщиком?   -  person bated    schedule 02.06.2016
comment
@bated: Спасибо, я только что обновил вопрос с вашими заметками, мне было интересно, нашел ли кто-нибудь другой подход к этой ситуации, а не просто добавил блоки кода, указанные Oracle.   -  person Eder    schedule 02.06.2016
comment
Не волнуйтесь. Я также заметил, что откат вашей транзакции находится в блоке try, что не рекомендуется, так как что произойдет, если возникнет исключение? Откат транзакции должен быть в блоке finally с использованием логического значения, которое устанавливается в значение true в начале блока try и устанавливается в значение false только в конце блока try. Я часто вижу это в коде ATG и вызывает повреждение порядка.   -  person bated    schedule 02.06.2016


Ответы (1)


Серия шагов, которую вы описали выше, является предписанной серией шагов для обновления заказа.

Не стесняйтесь учитывать это любым способом, который вы считаете полезным. Просто убедитесь, что при обновлении заказа вы или ваш унаследованный код выполнили необходимые шаги.

Один из распространенных способов, которым ATG выполняет аналогичный факторинг, заключается в том, что для данного метода, скажем, X(...), у вас будет метод preX(...), doX(...) и postX(...). Вы можете создать абстрактный класс со всем вашим шаблонным кодом в методах preX() и postX(), возможно, даже объявить final, и объявить doX() абстрактным. Затем ваш компонент будет наследовать от абстрактного класса и должен реализовать метод doX(). Возможно, вам также потребуется явно обрабатывать исключения.

Это, по сути, то, что делают стандартные обработчики форм (под разными именами).

Например;

public final void X(...) {
  preX(...); // call the pre method

  try {
    doX(...); // call the do method
  } catch (XException xe) {
    // handle error
  }

  postX(...); // call the post method
}

protected final void preX(...) {
  // do everything you need to do before your customer code
}

protected final void postX(...) {
  // do everything you need to do after your customer code
}

protected abstract void doX(...) throws XException;

Еще одна вещь, которую вы могли бы сделать вместо наследования от абстрактного класса, — это определить аннотацию, содержащую весь шаблонный код.

Третье, что вы могли бы сделать аналогичным образом, но гораздо сложнее втиснуть в свой код ATG, может состоять в том, чтобы определить аспект или перехватчик вызова метода с использованием сторонних фреймворков.

Однако, еще раз, что бы вы ни делали и как бы вы это ни делали, просто убедитесь, что вы выполняете все шаги.

person Vihung    schedule 20.06.2016
comment
Почему бы не использовать структуру, в которой они уже определены? Это именно то предложение, которое еще больше усложняет обслуживание/поддержку/модернизацию ATG! - person bated; 01.07.2016