tohokuaikiのチラシの裏

技術的ネタとか。

Confluenceで自動実行スケジュール化されたプラグインを作る

タスクを決められたタイミングで自動実行するプラグインを作ります。

管理画面の「スケジュール ジョブ」から実行できたり無効化できたりするやつです。
以下のドキュメントを参考にしました。

と、一番参考になったのはこれだったりします。
Confluence doesn't show plugin job on 'Scheduled Jobs' page - Atlassian Answers

atlassian-plugin.xml にJobを記述

  <job key="fooJob" name="jobの名前"
	  class="jp.junoe.confluence.plugins.FooJob"
	  perClusterJob="false"
	  description="jobの説明"/>
  <trigger key="FooJobTrigger" name="triggerの名前">
	  <job key="fooJob"/><!-- 上で書いたjobのキーと合わせる -->
	  <schedule cron-expression="*/20 * * * * ? *"/>
	  <!-- schedule repeat-interval="30000" repeat-count="5" / -->
	  <managed editable="true" keepingHistory="false" canRunAdhoc="true" canDisable="true"/>
  </trigger>
job要素

jobタグのperClusterJobというのは、クラスタ化したConfluenceで実行させるかどうかになります。
ソースコードを見ると、jobタグにはjitterSecsという要素も見られたのですが、

This parameter allows us to introduce some random jitter, this is predominately for on demand use to prevent many 1000s of servers from attempting to do similar things all at once, by providing a jitter value less than the size of the schedule frequency we spread the load caused by a process over time. This is only used on jitter enabled servers, at this stage, onDemand instances.

とあり、どうやらこれは何千台もクラスタ化をしているときに使うらしいです。とりあえず不要かと思います。メールの配信のJobで使われてたので、確かにメール配信をクラスタのすべてのConfluenceが一斉に行うとメールサーバに負荷がかかりすぎると思います。

trigger要素

スケジューリングを定義します。

  • trigger>jobのkeyで実行するJobのキーを指定します。
  • schedule要素でスケジュールの時刻設定をします。
  • cron-expressionでConfluenceのcron書式、repeat-interval(ミリ秒)とrepeat-countで単純な回数を指定できます。といっても、この回数がどうも微妙で5回と指定しても+1回多くなります。恐らく、Confluenceの起動時に何かをしたいという時に使うのかと思います。
  • managed要素は、管理画面から操作する際のオプションです。
    • editable : cronの時刻設定を管理画面のスケジュールジョブから変更できるかどうか。
    • keepingHistory : 履歴を保存するかどうか。この場合の保存というは、Confluenceを再起動しても保存されるかどうかという事。
    • canRunAdhoc : 管理画面から実行できるかどうか
    • canDisable : 管理画面から無効化できるかどうか

Jobクラスの作成

先ほど設定したJobクラスはAbstractJobを継承して

public class FooJob extends AbstractJob
{
    public void doExecute(JobExecutionContext jobExecutionContext) throws JobExecutionException
    {
       // ここにタスク処理を書く
    }
}

としてdoExecuteを実装すれば出来上がり。


・・・・Manager類はどうやってとるか?

これだけだと、PageManagerを初めとするManager類が一切使えません。そこで、JobDetailを使います。

atlassian-plugin.xml
  <component key="FooJobDetail" name="..."
	  class="jp.junoe.confluence.plugins.FooJobDetail">
	  <description>...</description>
	  <interface>org.quartz.JobDetail</interface>
  </component>

というようにコンポーネントを追加し、

  <trigger key="FooJobTrigger" name="triggerの名前">
	  <job key="fooJobDetail"/>

というようにTriggerのjobキー先をこのJobDetailに切り替えます。

JobDetailを継承したJavaクラス

このクラスにはインジェクションが可能なので

public class FooJobDetail extends JobDetail {

    private final BandanaManager bandanaManager;

    /**
     * Get the value of bandanaManager
     * @return 
     */
    public BandanaManager getBandanaManager() {
        return bandanaManager;
    }

    private PageManager pageManager;

    /**
     * Get the value of pageManager
     *
     * @return the value of pageManager
     */
    public PageManager getPageManager() {
        return pageManager;
    }

    public FooJobDetail(BandanaManager bandanaManager, PageManager pageManager) {
        super();
        setName(UpdateBlogInformationJobDetail.class.getSimpleName());
        setJobClass(UpdateBlogInformation.class);
        this.bandanaManager = bandanaManager;
        this.pageManager = pageManager;
    }
}

とします。このオブジェクトはプラグインインストール時の1回しか作られません。
毎回実行されるのはコンストラクタで実行しているsetJobClassで与えられたクラスのインスタンスです。
ですので、先ほどのJobをここで指定します。

Jobクラス

doExecuteの引数から先ほどインジェクションで取得した各種Managerを引っ張れます。

public class FooJob extends AbstractJob
{
    @Override
    public void doExecute(JobExecutionContext jobExecutionContext) throws JobExecutionException
    {
        JobDetail _jobDetail = jobExecutionContext.getJobDetail();
        FooJobDetail jobDetail = null;
        if (_jobDetail instanceof FooJobDetail) {
            jobDetail = (UpdateBlogInformationJobDetail) _jobDetail;
            BandanaManager bandanaManager = jobDetail.getBandanaManager();
            PageManager pageManager = jobDetail.getPageManager();
        }

    }
}