tohokuaikiのチラシの裏

技術的ネタとか。

ConfluenceのPlugin作成でバックグラウンドで走らせるようなタスクを作る

XMLエクスポートやインポートの時に出てくるプログラスバーがあるあれです。

ソースコード見てたらTransactionTemplateを使ってあれこれやるらしいのだけど、まんまコピペ―したらエラーが出た。

エラーの内容は、この人と一緒。answers.atlassian.com


で、原因はclassloaderの混合。Confluenceコアでは org.springframework.transaction.support.TransactionTemplate を使うのだけど、プラグんでは使っているSpringが違うとかでこれが使えない。そのため、com.atlassian.sal.api.transaction.TransactionTemplate を使う。そのあたりはこの辺で説明されてた。
Using Transaction Template - Atlassian Answers
Managing a Session Manually - Atlassian Answers

あるいは、Job Module - Atlassian Developersこのあたりのドキュメントでも良いかもしれない。

で、解決策としては

とりえあず、TransactionTemplateオブジェクトを作る。これはInjectionで作れるのでそれで作ってしまおう。

XWork/Webworkならセッターを用意して

   private TransactionTemplate transactionTemplate;

    public void setTransactionTemplate(TransactionTemplate tt) {
        this.transactionTemplate = tt;
    }

Servletならコンストラクタ引数にTransactionTemplate transactionTemplateとでも入れておけばできる。

タスクマネージャを作って、タスクの生成

時間のかかる処理を実装するタスクオブジェクトを設計。コンストラクタで先ほどのtransactionTemplateを持たせてメソッドrunInternalを実装する。こんな感じ。

public class FooLongRunningTask extends ConfluenceAbstractLongRunningTask {
   @Override
    protected void runInternal() {

        transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction() {
                // ここに時間のかかる処理
            }
        });

}

最後にタスクの実行

LongRunningTaskManagerをインジェクションで取得して、先ほどのタスクをオブジェクトにして

        if (isLongRunningTaskSupported() && !isSynchronous()) {
            taskId = longRunningTaskManager.startLongRunningTask(getAuthenticatedUser(), task);
            log.info("Started log-running task {} for export of space {}", taskId, getSpaceKey());
        } else {
            log.info("Starting synchronous export of space {}; long-running tasks not enabled", getSpaceKey());
            task.run();
        }

として実行。isLongRunningTaskSupported()とかは良く分からないけどとりあえずXMLエクスポートのコードをコピペで。

プログレスバーの変更

XWorkのresultを

<result name="success" type="dispatcher">/longrunningtask.action?taskId=${taskId}</result>

とすると、Submit後の画面がプログレスバーの画面になる。
プログレスバーの操作は、さっきの doInTransaction() の実装において、progressオブジェクトをあれこれ操作するとできる。これ自体は簡単なので省略。