このブログでは、Nuxeoで非常によく使うタスク、つまり、サーバサイドで定期的にビジネスロジックを実行する方法についてお話しします。

この機能は、様々な目的に使うことができます。週間レポートや月間レポートを生成する、古くなったドキュメントを四半期に一度「清掃」する、あるいは外部サーバに毎日1回接続する、などです。

その仕組みは非常に単純です。
スケジューライベントを定期的に送信します。 ハンドラがそのイベントに割り当てられ、イベントが開始されるたびに必要なタスクを実行します。

スケジューラ、イベント、ハンドラ:これらは、Nuxeo Studioで実に簡単に設定できます。Nuxeo Studioは地球上で最高のツールです(過去の記事でも何度かそう呼んだかもしれません)。

この記事では、「失効したアセットの処理」を例として使いましょう。

仮に私がピクチャビデオオーディオというドキュメントを持っていて、そのうちいくつかに失効日(フィールドdc:expired)が設定されているとしましょう。これらのアセットは、すべての社員に提供されていますが、失効日になるとロジックが適用されなければなりません。ここではロジックをシンプルにするため、失効後はライフサイクルの次の状態(「obsolete」)に移動し、許可を変更することで、管理者と検証グループのメンバーだけがアクセスできるようにすることにします。失効後のアセットの処理方法はいくつもありますが、どれを選ぶかは、御社のビジネスルールやプロセスによって変わってくるでしょう(上記は一例にすぎません)。

次に、以下の要素を少し詳しく見てみましょう。
まずはスケジューラ(これがすべての要です) 次にハンドラ(この例の自動化チェーンの1つにすぎません)
*最後にイベント

##スケジューラ
Nuxeoは、コンポーネントモデルを基本としていて、すべてがプラグインであり、すべてが拡張ポイントで設定できるアーキテクチャを有しています。しかも、最高のコンテンツサービスプラットフォームであるNuxeoには、数百というサービスに加えて設定可能なスケジューラのサービスがあり、必要なスケジュールをいくつでも追加することができます。

スケジューラの仕組みも非常にシンプルです。以下の4つのプロパティを少なくとも定義します。
スケジューラの固有のID。面白いことに、これでNuxeoのデフォルトのスケジューラを上書きすることができます。例えば、nuxeo-imap-connectorが5分ごとに新しいメールをチェックしていて、この頻度を変えたいのであれば、その定義を貼り付けて、cronExpressionの部分を変更します(下記参照)。 送信するイベントのID
イベントのカテゴリ(「default」で構いません) イベントの実行頻度を定義するcronの表現

文化的な注釈:開発者は無教養だと侮ることなかれ。語源に興味を抱くこともあります。Wikipediaで「cron」の語源を調べたところ、次のような説明が見つかりました。「cronの語源は、ギリシャ語で『時間』を意味する『χρόνος (chronos)』である」。なるほど、納得です。

使用すべき表現は、ドキュメンテーションのこちらのページをご覧ください(例も記載されています)。今回の例では、失効したアセットを毎日1回、午前3時に確認することにします。cronExpressionは次のようになります。
* * 3 * * ?
これを読み解くと、(左から順に)0秒、0分、3時、任意の日、任意の月、任意の曜日であることを意味します。

皆さんはどうか知りませんが、私自身は午前3時に起き出してスケジューラの動作を確認したいと思うようなタイプではありません。動作していなかった場合に、修正して翌日まで待ち、また午前3時に起きて確認するのも、やっぱり気が進みません。それに、デモが2日後に迫っていたら、そんな時間はありません。

そこで、テスティングのシナリオでは、このスケジューラを30秒ごとに実行することにします。次の表現を使うと、hh:mm:00sとhh:mm30s、すなわち毎分0秒と毎分30秒に実行されるようになります。このXML拡張構文をStudioのプロジェクトに貼り付けます(CONFIGURATION > Advanced Settings > XML Extensions)。

<extension
           target="org.nuxeo.ecm.core.scheduler.SchedulerService"
           point="schedule">
  <schedule id="`HandleExpiredAssets`">
    <event>`HandleExpiredAssets`</event>
    <eventCategory>default</eventCategory>
    <!-- Every day at 3am: * * 3 * * ? →
    <!-- For our test, let's do it every 30s at hh:mm:00 and hh:mm:30-->
    <cronExpression>0/30 * * * * ?</cronExpression>
  </schedule>
</extension>

これで、「HandleExpiredAssets」というIDの新しいスケジューラが定義できました。これは30秒ごとに「HandleExpiredAssets」イベントを起動します。ここではスケジューラIDとイベントIDに同じ値を使用しましたが、これは必須ではありません。

これでスケジューラの部分が完了しました。簡単でしょう? 実際、慣れてしまえば15秒でこのスケジューラを作れるようになります。Nuxeoのすごさが、またひとつ明らかになりました!

##ハンドラ
次は、失効したアセットを処理する自動化のチェーンを書きます。以下のことをするだけです。
1.「dc:expired」フィールドに今日よりも前の日付が入っているアセットで、かつ未処理のものを検索する
2.検出されたアセットを処理する

この例では、次のように処理します。
1.ライフサイクルの状態を変更する
2.許可を変更して、管理者か検証者だけがアクセスできるようにする

新たに失効したアセットだけを処理する方法が必要ですので、これにはライフサイクルの状態を使用します。ライフサイクルのポリシーは、ピクチャ、ビデオ、オーディオのドキュメントを対象とします。

Handler lifecyle

この例では、「approved」の状態になっているアセットのみを検索して、「obsolete」に変えます。これは、この自動化プロセスを設定する際に使用するNXQLクエリです(いつもどおり、バージョンやプロキシは変更しないことにします)。

SELECT * FROM Document WHERE
ecm:primaryType IN ('Picture','Video', 'Audio')
AND ecm:currentLifeCycleState = 'approved'
AND dc:expired < DATE '@{CurrentDate.format("yyyy-MM-dd")}'
AND ecm:isVersion = 0 AND ecm:isProxy = 0

ライフサイクルの状態を変更するのは簡単です。「Document.FollowLifecycleTransition」を使うだけです。

次は許可の変更ですが、これには以下のことが含まれます。
「local」の許可を削除する(存在する場合) 継承をブロックする(これでこのドキュメントを見られる唯一のグループとして管理者が追加されます)
*検証者のための新しい許可を作成する

このレベルの自動化は、実にパワフルです! このオペレーションですべての魔法を行わせ、ドキュメントの全リストの処理を自動化することで、多大な時間とリソースが節約できます。

ここで注意すべき重要な点が2つあります。どちらも、チェーンがスケジューラから実行されることに関係しています。実行に際しての_文脈_、すなわち「CoreSession」がないことです。このため、Studioがデフォルトで追加する「Fetch > ContextDocument」のオペレーションを最初に削除する必要があります。次に、クエリを実行する前に「LoginAs」を呼び出す必要があります。「LoginAs」が「CoreSession」を作成するためです。以下に、参考として完全なチェーンを示します。私はログオペレーションを追加しましたが、これは起こっていることをモニターして、どのようにスケジューラが動作し、イベントを起動し、チェーンが実行されているかを確認するためです。

Handler Chain

なかなか良い感じですよね? これまでのところ、スケジューラとハンドラを設定しましたので、次はイベントを作成しましょう。これが、スケジューラとハンドラをつなぐ「糊」の役割を果たします。

##イベント
他の2つのオペレーションと同様に、イベントの設定も非常に簡単です。新しいイベントハンドラを作成して、次のことをさせるだけです。
1.「HandleExpiredAssets」イベントをリッスンする
2.イベントをキャッチしたら「HandleExpiredAssets」チェーンを実行する

もう、お気付きですね。私はどこでも同じ名前を付ける傾向があります。かえって混乱すると思う方は、ご自由に変更してください。

Studioでのイベントハンドラの作成は、非常にシンプルです。が、この例では、解決しなければならない小さな問題がひとつあります。

「HandleExpiredAssets」はカスタムイベントであって、デフォルト設定の一部ではないため、Nuxeoにはこれが何なのか、まったく分かりません。そこで、Studio Registriesを活用します。これは、Studioの未知の要素を宣言する場所です。これによりStudioは、関連するすべての場所に対してこの要素を提供するようになります。SETTING > Registries > Core Eventsとナビゲートして、例えば次のように、このカスタムイベントを追加します。

Registry

これで新しいイベントハンドラを作成して、このイベントを使って、Studioに表示できるようになりました。そこで、イベントを作成し、イベントと自動化チェーンを選択すれば、完成です!

Event Listener

##使って楽しむ
ここでご紹介したのは、非常に手早くできる設定の例でした。実際にやってみると、この記事を読むほうが時間がかかることが分かるでしょう!

私からのアドバイスとしては、すべて保存し、テストサーバで展開して、ログをモニターすることです。非常に特徴的な2つの要素を使っていることを忘れないでください。カスタムライフサイクルポリシーとカスタムグループ(検証者)です。場合によっては、ピクチャを1つ開いてみて、その「dc:expired」フィールドを昨日に変更し、ログに表示されることを確認し、さらに30秒後に今度はログに表示されないことを確認してみるとよいかもしれません。

これは単純な例でしたが、御社のビジネスルールはおそらくもっと複雑でしょう。とはいえ、Nuxeo Studioがあれば、必要なものが何であれ構築できるツールを持つことになります。例えば、失効の1週間前にメール通知を送信して、ユーザがそのアセットを確認し必要に応じて失効日を変更したりするなどの処理をできるようにしたい場合もあるでしょう。

私だったら、失効日がはるか昔に過ぎていたことを上司に知られるような事態は避けたいと思います。