Традиционная обработка ошибок
ABL всегда был процедурным, блочно-структурированным языком, ориентированным на работу с базами данных. Язык позволяет смешивать логику процедурной обработки, пользовательского интерфейса и доступа к данным в одной процедуре. Ориентированность на работу с базами данных означает, что обработка ошибок в ABL превыше всего ставит защиту данных, работая в тесном взаимодействии с механизмом транзакций.
Язык ABL был расширен, чтобы обеспечить объектно-ориентированное программирование, программирование распределенных приложений и широкий набор интеграционных возможностей. Традиционная модель обработки ошибок обеспечивает удобный набор средств для обычного программирования на ABL, а также стандартный способ передачи ошибок от интеграционных компонентов платформы OpenEdge. Так, Web service возвращает ошибки в формате промышленного стандарта SOAP, при этом приложение может обрабатывать такие ошибки как системные ошибки ABL.
Ошибки операторов
Когда AVM не в состоянии корректно выполнить оператор ABL, в большинстве случаев устанавливается состояние ERROR. Если ошибка достаточно серьезна, может быть установлено состояние STOP (например, оператор RUN не может найти процедуру). Некоторые операторы никогда не устанавливают состояние ERROR, например, оператор MESSAGE.
Единого правила реакции операторов на ошибку не существует – для каждого оператора предусматривается наиболее «разумная» реакция, в зависимости от назначения оператора:
- Ошибка в FIND означает, что данные будут недоступны для следующих операторов – разумно установить состояние ERROR.
- Ошибка оператора RUN означает, что какая-то функция недоступна – разумно остановить приложение.
- Оператор MESSAGE обеспечивает обратную связь и должен вывести как можно больше информации – разумно не устанавливать состояние ошибки даже если невозможно «вычислить» все выражения в операторе.
Реакция каждого оператора ABL на ошибки описана в OpenEdge Development: ABL Reference – не ленитесь проверять, особенно для специализированных операторов и методов системных объектов.
Когда происходит ошибка при выполнении некоторого оператора, AVM останавливает выполнение блока, в котором находится этот оператор. То есть следующий оператор в теле блока не будет выполнен. Будет выведено стандартное сообщение об ошибке, а затем выполнена обработка ошибки на уровне блока, указанная явно или определенная по умолчанию. Эта обработка не может отменить остановку выполнения блока или выдачу сообщения об ошибке – это возможно только с помощью указания опции NO-ERROR в операторе.
Сообщения об ошибках OpenEdge
Для информирования пользователя об ошибках при выполнении процедур AVM выдает системные сообщения об ошибках времени выполнения (Run-time).
Каждое сообщение OpenEdge имеет уникальный номер и текст сообщения, кратко описывающий проблему:
Error string Error number **FIND FIRST/LAST failed for table Customer (565)
Подробная информация о конкретном сообщении об ошибке может быть получена с помощью Help-системы OpenEdge. Как правило, этой информации достаточно, для понимания проблемы. В противном случае, следует использовать KnowledgeBase и другие источники информации.
Программирование обработчика ошибок
Инструментом для собственной обработки ошибок в блоке ABL является фраза ON ERROR, определяемая в заголовке блока:
ON ERROR UNDO [ label1 ]
[ , LEAVE [ label2 ]
| , NEXT [ label2 ]
| , RETRY [ label1 ]
| , RETURN [ return-value |ERROR [ return-value | error-object-expression ] | NO-APPLY ]
| , THROW ]
Синтаксис практически идентичен синтаксису оператора UNDO, описанному выше.
Здесь:
label1 Метка блока, который требуется откатить. Если метка не указана, будет выполнен откат блока, в котором определена фраза ON ERROR.
LEAVE [ label2 ] Указывает, что, после отката блока, необходимо покинуть блок с меткой label2. Если метка label2 не указана, произойдет выход из блока с меткой label1 (или, если label1 тоже не указана – из текущего блока ON ERROR).
NEXT [ label2 ] Указывает, что, после отката блока, необходимо выполнить следующую итерацию блока с меткой label2. Если метка label2 не указана, будет выполнена следующая итерация блока с меткой label1. Если следующая итерация не может быть выполнена (например, уже была прочитана последняя запись), будет выполнен выход из блока.
RETRY [ label1 ] Указывает, что, после отката блока, необходимо повторить ту же самую итерацию блока с меткой label1. RETRY является действием по умолчанию, если явно не указано LEAVE, NEXT, RETRY, или RETURN. Если блок не содержит операторов ввода, AVM проверяет это и заменяет RETRY на LEAVE или NEXT (для итерационного блока).
RETURN … Выполняется возврат в вызывающую процедуру, или, если вызывающей процедуры нет, в OpenEdge Editor (или выход в операционную систему).
THROW В структурной обработке ошибок – передает ошибку для обработки внешнему блоку. Подробно будет описано ниже.
Следующая таблица (Таблица 5) описывает различные способы использования RETURN:
Таблица 5. Вариации опции RETURN
Опция |
Описание |
return-value | Символьная строка, которую Вы передаете в вызывающую процедуру. Вызывающая процедура может прочитать это значение с помощью функции RETURN-VALUE, если возврат был из процедуры. Для функции тип значения должен соответствовать описанию функции – и функция RETURN-VALUE возвращает пустую строку. |
ERROR | Устанавливает ERROR в вызывающей процедуре (кроме возврата из функции – см. замечание ниже).
Не может быть использовано в триггере интерфейса или деструкторе. |
ERROR return-value | Устанавливает ERROR в вызывающей процедуре (кроме возврата из функции). С помощью функции RETURN-VALUE можно получить значение возвращаемой строки.
При структурной обработке ошибок также создается объект AppError и return-value сохраняется в его свойстве ReturnValue. |
ERROR error-object-expression | При структурной обработке ошибок устанавливает ERROR в вызывающей процедуре. Указанный объект представляет кодированную Вами ошибку. Если это объект AppError, его свойство ReturnValue доступно с помощью функции RETURN-VALUE. |
NO-APPLY | В триггере интерфейса – подавляет стандартную обработку события. |
Замечание: Возврат из пользовательской функции с помощью RETURN ERROR … не устанавливает состояние ERROR в вызывающей процедуре. При этом функция возвращает неопределенное значение. Значение return-value (если указано RETURN ERROR return-value) недоступно через функцию RETURN-VALUE. То есть имеется некоторая непоследовательность в реализации языка.
Опции фразы ON ERROR соответствуют свойствам обработки ошибок блока. Можно считать, что все блоки, кроме простого DO-блока, имеют неявную ON ERROR фразу. Так, можно считать, что для процедурного блока (который не поддерживает явную фразу ON ERROR), неявно указано ON ERROR UNDO, LEAVE или ON ERROR UNDO, RETRY (если процедура содержит операторы ввода).
Использование меток.
Рассмотрим пример (Программа 25). Эта программа содержит достаточно общий набор вложенных блоков, выводящих список номеров заказов для нескольких клиентов из базы Sports2000. В самом внутреннем блоке бессмысленный оператор FIND вызывает ошибку в конце первой итерации. Этот простой пример позволяет проанализировать взаимодействие обработчиков ошибок ON ERROR. Первоначальная версия демонстрирует, как откатить транзакцию во внешнем блоке, вместо отката субтранзакции во внутреннем.
Программа 25. Использование меток
PROCEDURE NestedBlocks: Outer-Block: FOR EACH Customer WHERE CustNum < 5: ASSIGN Customer.Name = Customer.Name + “_changed”. Inner-Block: FOR EACH Order OF Customer ON ERROR UNDO Outer-Block, RETURN: DISPLAY OrderNum. /* Nonsense code raises ERROR. */ FIND SalesRep WHERE RepName = Customer.Name. END. /* Inner-Block */ END. /* Outer-Block */ DISPLAY "For Blocks Complete". END PROCEDURE. RUN NestedBlocks. DISPLAY "Procedure NestedBlocks Complete."
При выполнении этого примера происходит следующее:
- оператор ASSIGN в блоке Outer-Block начинает транзакцию;
- оператор FIND в блоке Inner-Block устанавливает состояние ERROR;
- блок Inner-Block автоматически откатывается;
- активируется явная фраза ON ERROR в блоке Inner-Block;
- откатывается блок Outer-Block;
- управление возвращается вызывающей программе, которой является основной блок процедуры.
В следующей таблице (Таблица 6) приведен список всех действующих в блоках фраз ON ERROR:
Таблица 6. Действующие фразы ON ERROR
Блок |
Фраза ON ERROR |
Процедурный блок (.p файл) | Неявная, ON ERROR UNDO, LEAVE |
Внутренняя процедура NestedBlocks | Неявная, ON ERROR UNDO, LEAVE |
Блок FOR EACH, с меткой Outer-Block | Неявная, ON ERROR UNDO, NEXT |
Блок FOR EACH, с меткой Inner-Block | Явная, ON ERROR UNDO Outer-Block, RETURN |
Когда на первой итерации Inner-Block устанавливается состояние ERROR, явная фраза ON ERROR приводит к откату Outer-Block и выполнению RETURN. RETURN возвращает управление вызывающей процедуре, в данном случае – файлу процедуры. Так как в RETURN не указано ERROR, состояние ERROR не устанавливается в вызывающей процедуре – и мы видим исполнение последнего оператора DISPLAY.
Если изменить фразу ON ERROR в Inner-Block:
Inner-Block: FOR EACH Order OF Customer ON ERROR UNDO Outer-Block, RETURN ERROR:
– то поведение программы почти не изменится, но последний оператор DISPLAY не будет выполнен. Установка состояния ERROR в вызывающей процедуре возвратит управление в OpenEdge Editor.
Если использовать опцию перехода NEXT:
Inner-Block: FOR EACH Order OF Customer ON ERROR UNDO Outer-Block, NEXT:
– то для каждой записи Customer Вы будете видеть одну запись Order, сопровождаемую сообщением об ошибке.
Наконец, если удалить явную фразу ON ERROR в Inner-Block, то неявная фраза для него будет ON ERROR UNDO, NEXT. Откат будет выполняться для блока Inner-Block, и Вы будете видеть сообщение об ошибке для каждой итерации Inner-Block для каждой записи Customer.