UiPath Developer Community 第12回ワークショップ 覚え書き。例外処理のfinally節問題。


## 概要

[UiPath Developer Community 第12回ワークショップ](https://www.uipath.com/ja/resources/knowledge-base/tag/japan-developer-community) で聞いてきた内容の覚え書き。。
いってきました。第12回。今回は [LTへ登壇させてもらったり](https://qiita.com/masatomix/items/8ef146486a87686a9345) といろいろあったんですが、いちばん驚いたのが、、下記の例外処理について。


## 残念なお知らせ(として説明されていた内容)

コレです。

「try/catchアクティビティは、**try節、catch節、いずれかを最後まで実行**しないと、finally節が呼び出されない」というナゾ仕様のはなし。
![01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73777/70316e2c-9038-28d1-6f2b-194721a9076d.jpeg)
![02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73777/52afd86c-9de4-7cc9-f1b6-8f5e83f95cce.jpeg)

いやいや、、、ありえないっしょ、、、。ということでやってみました。
![S01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73777/185e307b-229d-daa4-bebb-10964b409319.png)
![S02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73777/fa92e178-88d4-4d2b-fe7e-02f9dab0a9e8.png)

Tryで例外をスローするコード。Finally節がよばれれば「Finally.」が出力されるはずです。

実行してみると、、
![S03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73777/962ed4c6-5fef-59f6-bc95-bf3f87a43390.png)


うん、ホントだ実行されてない。
**ありえない仕様**だと思います。。しりませんでした。


try/finallyでなくてtry/catch/finally とした場合も、catchで再度例外をスローすると、**try/catchいずれも最後まで実行できていない**ので、同じ結果となります。これは「例外を再度throw」でも「再スローのアクティビティ」でも「new Exception("hoge",exception)」など別の例外にWrapしてスローしても、結果は同じです。


「try節、catch節、いずれかを最後まで実行しないと、finally節が呼び出されない」ってことは**例外が発生しないで正常終了するか、発生しても上(呼び元)に投げない(ってつまり正常終了じゃん) 場合のみfinallyがよばれる**ってことですよね。。ちょっと意味が。。。ホントに残念なお知らせだと思います。


### ちなみにJavaやC#だと


ちなみに、たとえばJavaだと、try/catch のfinally節は基本的に try/catch節がどうなろうと、必ず呼び出されます。

- 例1: tryを最後まで実行しないとき
 
```java
public class Main {
    public static void main(String[] args) {
        System.out.println("UiPathだとFinallyがよばれない例1");
        execute1();
    }

    private static void execute1() {
        try {
            System.out.println("Start.");
            throw new RuntimeException("Error!");
        } finally {
            System.out.println("Finally.");
        }
    }
}
```

実行すると、

```bash
Exception in thread "main" 
UiPathだとFinallyがよばれない例1
Start.
Finally.
java.lang.RuntimeException: Error!
	at Main.execute1(Main.java:10)
	at Main.main(Main.java:4)
```

うん、当然こうなります。


- 例2: tryを最後まで実行しない、かつcatchも最後まで実行しない とき
 
```java
public class Main {
    public static void main(String[] args) {
        System.out.println("UiPathだとFinallyがよばれない例2");
        execute2();
    }

    private static void execute2() {
        try {
            System.out.println("Start.");
            throw new RuntimeException("Error!");
        } catch (RuntimeException e) {
            throw e;
        } finally {
            System.out.println("Finally.");
        }
    }
}
```

実行すると、

```bash
UiPathだとFinallyがよばれない例2
Exception in thread "main" 
Start.
Finally.
java.lang.RuntimeException: Error!
	at Main.execute2(Main.java:10)
	at Main.main(Main.java:4)
```


当然C#でも、Javaとおなじ。

```c#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("UiPathだとFinallyがよばれない例1");
            execute1();
        }

        private static void execute1()
        {
            try
            {
                Console.WriteLine("Start.");
                throw new Exception("Error!");
            }
            finally
            {
                Console.WriteLine("Finally.");
                //Console.ReadKey();
            }
        }
    }
}
```


```
UiPathだとFinallyがよばれない例1
Start.

ハンドルされていない例外: System.Exception: Error!
   場所 ConsoleApp1.Program.execute2()
   場所 ConsoleApp1.Program.execute1()
   場所 ConsoleApp1.Program.Main(String[] args)
Finally.
```



### というわけで対応策

というわけでfinallyをちゃんと実行しつつ**上(呼びだし元)にエラーを投げたいときは、catchで例外を投げるのではなく、例外のインスタンスを一旦変数に保管し、finallyもしくはさらにその後で、その例外をスローします。** 
まずJavaで書くとこんな感じ。

```java
public class Main {
    public static void main(String[] args) {
        System.out.println("そのための対策をしたコード");
        execute3();
    }

    private static void execute3() {
        RuntimeException exception = null;
        try {
            System.out.println("Start.");
            throw new RuntimeException("Error!");
        } catch (RuntimeException e) {
            exception = e;
        } finally {
            System.out.println("Finally.");
            if (exception != null) {
                throw exception;
            }
        }
    }
}
```

実行してみると、

```bash
そのための対策をしたコード
Start.
Finally.
Exception in thread "main" java.lang.RuntimeException: Error!
	at Main.execute3(Main.java:11)
	at Main.main(Main.java:4)
```

こういうことですね。

UiPathでやるとこうです。try節は元のワークフローと同じ。トライキャッチのスコープであからじめ``exception``変数を定義しておき、catchでスローされた例外 ``e``を、その変数に格納します。
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73777/253a1149-a3c2-6a9e-4ee6-03b0020c9684.png)

そしてfinally節で、その変数がnullでなければ「例外が発生していたはず」ということであらためて例外をスローします。
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73777/7a643e39-d232-11d2-6500-b53b201dec08.png)

結果は、、、
![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73777/e537e412-94fb-ed78-7231-cd511b6ac7f1.png)

よさそうですね。

### そういえば Attended Frameworkもそうなってた :-)

そういえば、Attended Frameworkも``Process.xaml`` をtry/catchで囲んでる箇所も、まったく同じ構造になっていました。

Catchでは発生した例外を変数に入れておいて、
![T04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73777/4000b676-de46-b095-6c3f-cc141221fc76.png)

finallyで再度スローしてますね。
![T05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73777/3e0a7816-4f82-c38b-7c75-a8d30bcbba7b.png)

この処理の流れ、なんでこんなメンドイ流れなのかなー？？「正常終了と異常終了(業務とシステム例外)」の3パタンを一箇所に集めるためかなー？？って理解してたんですが、まさかfinallyをちゃんと呼べないバグの対応だったとは、、。。

なるほど、色々勉強になりました。

## 新たなユーザコミュニティについて

新しいユーザコミュニティをつくって、もっともっとユーザの声が届きやすくする動きがあるようですね！
まずは名前を決めましょうということで、名前を投票できるようです。下記のQRからいけるので興味がある方はアクセスしてみてください。

![com.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73777/26dfc822-ff5d-2903-0924-06392bff9acd.jpeg)


以上です。。今回の記事は、、例外処理の件というUiPath本体外のところが中心になっちゃいました。。

おつかれさまでした。 

## 関連リンク

- [Attended Framework テンプレートを使ってみた(Slide版)](https://qiita.com/masatomix/items/8ef146486a87686a9345) LTで登壇した資料です
- [UiPathの Attended Framework テンプレートを使ってみた](https://qiita.com/masatomix/items/1d96169a7c423b1939b0) その資料の元ネタ
- [UiPath Developer Community 第12回ワークショップ 覚え書き。例外処理のfinally節問題。](https://qiita.com/masatomix/items/d8839b21cb09513e19ef) 本記事
- [UiPath Developer Community 第11回ワークショップ 覚え書き AI Computer Vision とUiPath 2019 Fast Trackの新機能 紹介。](https://qiita.com/masatomix/items/76addb9de98a8321e82b)
- [UiPath Developer Community 第10回ワークショップ 覚え書き OrchestratorのWebhook連携、ほか。](https://qiita.com/masatomix/items/90393bdbb7824c551106)
- [UiPath Developer Community 第9回ワークショップ 覚え書き「Error Handling」や「Floating Robot」。](https://qiita.com/masatomix/items/38adb155327d287e4248)
- [UiPath Developer Community 第8回ワークショップ 覚え書き「カスタムアクティビティの、デバッガによるデバッグ方法について」](https://qiita.com/masatomix/items/d38226f52cff23454db4)
- [UiPath Developer Community 第7回ワークショップ 覚え書き「カスタム入力アクティビティ」](https://qiita.com/masatomix/items/2d6ac698ce14dd1e3324)
- [UiPath Developer Community 第6回ワークショップ 覚え書き「出現するタイミングが不定のポップアップを処理する」](https://qiita.com/masatomix/items/72422148ea8353662eb1)
- [System.Activities.Statements.TryCatch のソースコード](https://referencesource.microsoft.com/#System.Activities/System/Activities/Statements/TryCatch.cs)