10
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-08-06

概要

UiPath Developer Community 第12回ワークショップ で聞いてきた内容の覚え書き。。
いってきました。第12回。今回は LTへ登壇させてもらったり といろいろあったんですが、いちばん驚いたのが、、下記の例外処理について。

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

コレです。

「try/catchアクティビティは、try節、catch節、いずれかを最後まで実行しないと、finally節が呼び出されない」というナゾ仕様のはなし。
01.jpg
02.jpg

いやいや、、、ありえないっしょ、、、。ということでやってみました。
S01.png
S02.png

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

実行してみると、、
S03.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を最後まで実行しないとき
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.");
        }
    }
}

実行すると、

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も最後まで実行しない とき
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.");
        }
    }
}

実行すると、

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とおなじ。

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で書くとこんな感じ。

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;
            }
        }
    }
}

実行してみると、

そのための対策をしたコード
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

そしてfinally節で、その変数がnullでなければ「例外が発生していたはず」ということであらためて例外をスローします。
05.png

結果は、、、
06.png

よさそうですね。

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

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

Catchでは発生した例外を変数に入れておいて、
T04.png

finallyで再度スローしてますね。
T05.png

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

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

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

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

com.jpg

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

おつかれさまでした。

関連リンク

10
9
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?