Межмодульная передача ошибок в iJaNet Framework¶
Автор: Антон Сержантов
Компания: Janet systems
В JaNet Framework два модуля могут взаимодействовать друг с другом посредством вызова методов сервисов. Один модуль обращается к сервису другого модуля через прокси (подробнее см. здесь). Если в вызываемом методе сервиса возникла ошибка, то она передастся в точку вызова в вызывающий модуль в "чистом виде", если тип ошибки может быть приведен к одному из типов перечисленных в сигнатуре throws вызываемого метода. Иначе ошибка будет "упакована" проксей на клиентской стороне в UndeclaredThrowableException (подробнее см. здесь).
Рассмотрим вышесказанное подробнее не примере.
Пример межмодульной передачи ошибки в iJaNet Framework¶
Пусть в системе есть два модуля А и В. Пусть в модуле А вызывается сервис модуля В.
Метод сервиса:
1 public void Execute(){
2 try{
3 ...
4 }
5 catch(Throwable th){
6 throw new MyException("My service execution failed", th);
7 }
8 }
Объявление класса ошибки
1 ...
2 public class MyException extends Throwable{
3 ...
4 }
Вызов сервиса MyService в модуле А
1 ...
2 try{
3 MyService mySvc = (MyService)Services.getService("my_service");
4 mySvc.Execute();
5 }
6 catch(Throwable th) {
7 ...
8 }
9 ...
При таком объявлении метода Execute() в модуле А в блоке catch придет ошибка с типом UndeclaredThrowableException с вложенной ошибкой типа MyException, т.к. передается ошибка с типом MyException, а в сигнатуре метода не указано ни одного типа ошибок (нет выражения throws). Для того, чтобы в блок catch в модуле А пришла ошибка типа MyException, необходимо добавить в объявление метода Execute() вот такое выражение
1 public void Execute() throws MyException /*или throws Throwable*/{
2 try{
3 ...
4 }
5 catch(Throwable th){
6 throw new MyException("My service execution failed", th);
7 }
8 }
Теперь после объявления типа передаваемых ошибок, прокси в модуле А сможет привести пришедший тип ошибки к типу MyExcepiton (или Throwable).
Рекомендации по выбору типа ошибок, декларируемых в сигнатурах методов сервиса¶
В этой главе будет дана рекомендация по построению иерархии классов ошибок в системе iJaNet Framework. Правило всего одно и достаточно простое:
Все классы новых ошибок рекомендуется наследовать от типа jws.core.services.ServiceException и объявлении его в сигнатуре метода сервиса в выражении throws
Данная рекомендация связана с тем, что до момента вызова непосредственно метода сервиса могут возникнуть ошибки внутри платформы, связанные с инициализацией сервиса, создания экземпляра сервиса и т.д. Все эти ошибки упаковываются в jws.core.services.ServiceException и передаются в вызывающий модуль. Таким образом, если в сигнатуре метода сервиса не будет указан данный тип, то подобные ошибки будут оборачиваться в UndeclaredThrowableException проксей на вызывающей стороне.
В итоге, рекомендуемая иерархия классов ошибок должна выглядеть следующим образом
Рекомендуемая сигнатура методов сервиса
1 ...
2 public interface IMyService {
3 ...
4 public void method1() throws ServiceException;
5 public void method2() throws ServiceException;
6 ...
7 }
И соответственно обработка ошибок на вызывающей стороне может выглядеть так
1 ...
2 try{
3 IMyService mySvc;
4 ...
5 mySvc.method1();
6 ...
7 mySvc.method2();
8 ...
9 }
10 catch(MyCustomException ex1) {
11 ...
12 }
13 catch(MyCustomException1 ex2) {
14 ...
15 }
16 catch(ServiceException ex3) {
17 //Системная ошибка в инициализации сервиса
18 }
19 ...
Пример передачи ошибок в Remoting¶
Если речь идет о Remoting, то алгоритм передачи ошибок тот же, что и для сервисов с некоторой поправкой. Пусть, для примера, в системе также есть два модуля А и В, модуль А вызывает метод удаленного объекта модуля В
Метод Perform() удаленного объекта
1 public void Perform() throws MyRemoteObjectException
2 try{
3 ...
4 }
5 catch(Throwable th) {
6 throw new MyRemoteObjectException("Method Perform() invocation failed", th);
7 }
8 }
Класс ошибки
1 public class MyRemoteObjectException extends Throwable{
2 ...
3 }
При таких обстоятельствах на стороне, вызывающей метод Perform() удаленного объекта MyRemoteObject, ошибка MyRemoteObjectException в чистом виде не придет никогда. И вот такой блок catch
1 ...
2 try{
3 ... //вызов метода удаленного объекта
4 }
5 catch(MyRemoteObjectException ex){
6 ...
7 }
8 ...
9 }
никогда не сработает. Дело в том, что вызов метода удаленного объекта происходит через IRemotingService сервис, в частности через метод IRemotingService.invokeObject(...). Внутри этого метода все возникшие ошибки методов удаленных объектов "упаковываются" в RemotingException и передаются вызывающей стороне. Поэтому корректный код на стороне модуля А, вызывающий метод удаленного объекта, будет выглядеть так
1 ...
2 IRemoteObject myRO;
3 try{
4 myRO = Remoting.createObject("MyRemoteObject", "remoting"); //создание экземпляра класса MyRemoteObject на стороне модуля В через
5 //IRemotingService сервис с локальным именем 'remoting'
6 }
7 catch(RemotingException ex){
8 ... //ошибка создания прокси для удаленного объекта
9 }
10 try{
11 myRO.invoke("Perform", new TypedValue[0]); //Вызов метода
12 }
13 catch(RemotingException re){
14 MyRemoteObjectException myEx = (MyRemoteObjectException )re.getCause();
15 throw myEx;
16 }
17 ...