Out of Process SDKのサンプルコード(1) event-api-handlers - Out of Process SDKのサンプルコード(1) event-api-handlers - aegif Labo Blog Alfresco
null Out of Process SDKのサンプルコード(1) event-api-handlers
今回はOut of Process SDKに3つあるサンプルコードの中から、event-api-handlersというサンプルコードをご紹介します。
まずは適当なフォルダにOut of Process SDKプロジェクトをcloneします。
git clone https://github.com/Alfresco/alfresco-java-sdk.git
どの時点のコードを使った手順か明確にしておくために、執筆時点で最新のタグ7.0.0をチェックアウトします。
git checkout 7.0.0
今回はサンプルコードを実際に動かしてみて、Out of Process Extensionのイメージを掴んでもらうことが目的なので、早速サンプルコードを動かす手順をご紹介します。
event-api-handlersというサンプルコードはsamplesフォルダに入っているので、まずはそのフォルダに移動します。
cd samples/event-api-handlers
以下、event-api-handlersフォルダ配下のファイルを対象とします。
docker-compose.ymlを見ると、このサンプルコードで作成されるSpring Bootアプリケーションの他に、ACSやShareといったAlfrescoを動かすためのコンテナも定義されています。このまま使ってもよいのですが、実際の開発ではカスタムモデルをIn Process Extensionで実装したりするはずなので、今回はIn Process SDKで起動したAlfrescoに接続する方法を試します。
こちらの記事を参考に、プロジェクトを作成してAlfrescoを起動しておいてください。
次に、サンプルコードを起動するんですが、必要なサービスだけ起動するようにサンプルコードに少し手を加えます。
まず、docker-compose.ymlを開き、以下の部分を残して全てコメントアウトします(削除してもOKです)。
version: "2"
services:
event-api-handlers-sample:
image: event-api-handlers-sample:development
build:
context: .
次に、resources/application.properties を開き、spring.activemq.brokerUrlを以下のように書き換えます。
# spring.activemq.brokerUrl=tcp://activemq:61616
spring.activemq.brokerUrl=tcp://host.docker.internal:61616
これで、サンプルコードのSpring Bootアプリケーションが、In Process SDKのプロジェクトで起動したAlfresco(厳密にはそのAlfrescoと連携しているActiveMQ)に接続するようになります。host.docker.internalはMacやWindowsのDocker Desktopでしか使えないようなので、それ以外の環境の方はコンテナからホストにアクセスする方法を調べていただくか、In Process SDKのAlfrescoに接続することを諦めて元のdocker-compose.ymlに戻して試していただければと思います。
ここでActiveMQのURLを指定する理由については、公式ドキュメントの図を見ていただくとイメージが掴みやすいと思います。Content Services(Platform)で発生したイベントがActiveMQに登録され、そのイベント情報をActiveMQから取得して利用するという流れになります。
最後に、依存関係を解決できるようにREADMEの指示に従ってpom.xmlに以下のrepositoryを追記します。
<repositories>
<repository>
<id>alfresco-public</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
</repository>
</repositories>
以上でサンプルコードを実行する準備は完了です。が、この後アプリケーションを起動して動作確認を行う時に、どの操作に対するログなのか判別できない箇所が1つあるので、追加でノードの名称をログに出力するように手を加えておきます。MultipleEventTypeHandler.javaに以下のようにimport文とログ出力の行を追記します。
import org.alfresco.repo.event.v1.model.NodeResource; // import文を追記
...
@Override
public void handleEvent(final RepoEvent<DataAttributes<Resource>> event) {
LOGGER.info("Multiple event handler detecting an event of type {}", event.getType());
LOGGER.info("Node Name: {}", ((NodeResource) event.getData().getResource()).getName()); // この行を追記
}
以上で準備は完了です。run.sh(Windowsの場合はrun.bat)を使い、アプリケーションを起動します。
./run.sh build_start
起動したらターミナルにログが出力されている状態になると思うので、この状態でAlfresco上のフォルダやファイルに変更を加えてみて、どのようなログが出力されるか観察します。
ノードの作成
ブラウザで http://localhost:8180/share を開き、admin/adminでログインします。ヘッダの「共有ファイル」を開き、「テスト」という名前でフォルダを作成します。
すると、サンプルアプリケーションに以下のようなログが出力されます。
event-api-handlers-sample-1 | 2025-01-09T08:09:43.098Z INFO 1 --- [erContainer#0-1] .a.s.s.e.EventsHandlersSampleApplication : First Event handler triggered on node created - Event: RepoEvent [specversion=1.0, type=org.alfresco.event.node.Created, id=b6303e49-01be-4d5e-b62c-7b01b57704f1, source=/048a2312-511b-4e9b-8a23-12511b5e9b78, time=2025-01-09T08:09:43.084Z, dataschema=https://api.alfresco.com/schema/event/repo/v1/nodeCreated, datacontenttype=application/json, data=EventData [eventGroupId=49330fb7-ebcc-449c-b30f-b7ebcc549c7e, resource=NodeResource [id=45cfd87c-16f4-4414-8fd8-7c16f4f41461, name=テスト, nodeType=cm:folder, isFile=false, isFolder=true, createdByUser=UserInfo [id=admin, displayName=Administrator], createdAt=2025-01-09T08:09:43.072Z, modifiedByUser=UserInfo [id=admin, displayName=Administrator], modifiedAt=2025-01-09T08:09:43.072Z, content=null, properties={cm:title=, cm:description=}, localizedProperties={cm:title={ja=}, cm:description={ja=}}, aspectNames=[cm:titled, cm:auditable], primaryHierarchy=[c5e2311c-96db-4624-a231-1c96db2624ab, 6afc954c-7419-4942-bc95-4c74199942d8, 30020c87-c65b-4e72-820c-87c65bce7249], primaryAssocQName=cm:テスト, secondaryParents=[]], resourceBefore=null, resourceReaderAuthorities=null, resourceDeniedAuthorities=null, resourceReaderSecurityControls=null], extensionAttributes=null]
event-api-handlers-sample-1 | 2025-01-09T08:09:43.099Z INFO 1 --- [erContainer#0-1] .a.s.s.e.EventsHandlersSampleApplication : Second Event handler triggered on node created - Event: RepoEvent [specversion=1.0, type=org.alfresco.event.node.Created, id=b6303e49-01be-4d5e-b62c-7b01b57704f1, source=/048a2312-511b-4e9b-8a23-12511b5e9b78, time=2025-01-09T08:09:43.084Z, dataschema=https://api.alfresco.com/schema/event/repo/v1/nodeCreated, datacontenttype=application/json, data=EventData [eventGroupId=49330fb7-ebcc-449c-b30f-b7ebcc549c7e, resource=NodeResource [id=45cfd87c-16f4-4414-8fd8-7c16f4f41461, name=テスト, nodeType=cm:folder, isFile=false, isFolder=true, createdByUser=UserInfo [id=admin, displayName=Administrator], createdAt=2025-01-09T08:09:43.072Z, modifiedByUser=UserInfo [id=admin, displayName=Administrator], modifiedAt=2025-01-09T08:09:43.072Z, content=null, properties={cm:title=, cm:description=}, localizedProperties={cm:title={ja=}, cm:description={ja=}}, aspectNames=[cm:titled, cm:auditable], primaryHierarchy=[c5e2311c-96db-4624-a231-1c96db2624ab, 6afc954c-7419-4942-bc95-4c74199942d8, 30020c87-c65b-4e72-820c-87c65bce7249], primaryAssocQName=cm:テスト, secondaryParents=[]], resourceBefore=null, resourceReaderAuthorities=null, resourceDeniedAuthorities=null, resourceReaderSecurityControls=null], extensionAttributes=null]
これは、EventsHandlersSampleApplication.javaの以下のコードの出力内容になります。@Beanアノテーションを付けてOnNodeCreatedEventHandlerをBeanとして登録しており、取得したイベント(repoEvent)の内容をログに出力するという処理内容になっています。
また、@Orderアノテーションを付けることで実行される順番を指定しています。上記のログを見ると、確かに First Event handler triggered on node created ... というログが先に出力されていることがわかります。
/**
* This event handler definition illustrates how you can use Spring's {@link Order} annotation to sort the execution of event handlers.
*/
@Bean
@Order(10)
public OnNodeCreatedEventHandler firstCustomNodeCreatedEventHandler() {
return repoEvent -> LOGGER.info("First Event handler triggered on node created - Event: {}", repoEvent);
}
/**
* This event handler definition illustrates how you can use Spring's {@link Order} annotation to sort the execution of event handlers.
*/
@Bean
@Order(20)
public OnNodeCreatedEventHandler secondCustomNodeCreatedEventHandler() {
return repoEvent -> LOGGER.info("Second Event handler triggered on node created - Event: {}", repoEvent);
}
また、以下のログも出力されます。
event-api-handlers-sample-1 | 2025-01-09T08:09:43.100Z INFO 1 --- [erContainer#0-1] o.a.s.s.e.h.MultipleEventTypeHandler : Multiple event handler detecting an event of type org.alfresco.event.node.Created
event-api-handlers-sample-1 | 2025-01-09T08:09:43.100Z INFO 1 --- [erContainer#0-1] o.a.s.s.e.h.MultipleEventTypeHandler : Node Name: テスト
event-api-handlers-sample-1 | 2025-01-09T08:09:43.110Z INFO 1 --- [erContainer#0-1] o.a.s.s.e.h.MultipleEventTypeHandler : Multiple event handler detecting an event of type org.alfresco.event.node.Updated
event-api-handlers-sample-1 | 2025-01-09T08:09:43.110Z INFO 1 --- [erContainer#0-1] o.a.s.s.e.h.MultipleEventTypeHandler : Node Name: Shared
このログは以下のMultipleEventTypeHandlerから出力されています。OnNodeCreatedEventHandlerインターフェースとOnNodeUpdatedEventHandlerインターフェースを実装してgetHandledEventTypesをオーバーライドしており、ノードが作成された時(EventType.NODE_CREATED)と更新された時(EventType.NODE_UPDATED)にhandleEventが実行されます。このクラスのように複数のEventHandlerインターフェースを実装した場合はgetHandledEventTypesをオーバーライドしてどのEventTypeの時に実行するか指定する必要がありますが、実装するインターフェースが1つであればgetHandledEventTypesはインターフェース内で定義されているのでオーバーライドする必要はありません(後述するContentTypeNodeCreatedHandlerやContentUpdatedHandler等)。
このクラスではhandleEventにはイベントからノードの名前をログに出力する処理が書かれています。上記のログの1〜2行目が「テスト」フォルダが作成されたというイベントのログで、3〜4行目が「共有ファイル」フォルダが更新されたというイベントのログです(「Shared」は「共有ファイル」フォルダのノードの名称です)。フォルダやファイルを作成した時に、その親フォルダに対してもイベントが発火していることがわかります。準備でMultipleEventTypeHandlerにノードの名称をログに出力する行を追記しておいたのは、2行目と4行目のログを出力してどのノードに対して発火したイベントなのかを見分けられるようにしたかったためです。
@Component
public class MultipleEventTypeHandler implements OnNodeCreatedEventHandler, OnNodeUpdatedEventHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(MultipleEventTypeHandler.class);
@Override
public void handleEvent(final RepoEvent<DataAttributes<Resource>> event) {
LOGGER.info("Multiple event handler detecting an event of type {}", event.getType());
LOGGER.info("Node Name: {}", ((NodeResource) event.getData().getResource()).getName());
}
@Override public Set<EventType> getHandledEventTypes() {
return Set.of(EventType.NODE_CREATED, EventType.NODE_UPDATED);
}
}
以下の動作確認でも上記のEventsHandlersSampleApplicationとMultipleEventTypeHandlerのログは出力されますが、同じ内容なので説明は省略します。
コンテンツの作成
先ほど作成した「テスト」フォルダに「test.txt」というファイルを作成すると、以下のログが出力されます。
event-api-handlers-sample-1 | 2025-01-09T08:27:53.102Z INFO 1 --- [erContainer#0-1] .a.s.s.e.h.ContentTypeNodeCreatedHandler : A new node named test.txt of type cm:content has been created!
このログは以下のContentTypeNodeCreatedHandlerから出力されています。OnNodeCreatedEventHandlerインターフェースを実装しており、ノードが作成された時にhandleEventが実行される点は先ほどのMultipleEventTypeHandlerと同じですが、getEventFilterでタイプが cm:content のノードに対するイベントのみフィルタリングしています。そのため、タイプがcm:contentのノードが作成された時にhandleEventが実行されます。handleEventにはイベントからノードの名前をログに出力する処理が書かれています。
@Component
public class ContentTypeNodeCreatedHandler implements OnNodeCreatedEventHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ContentTypeNodeCreatedHandler.class);
@Override
public void handleEvent(final RepoEvent<DataAttributes<Resource>> repoEvent) {
LOGGER.info("A new node named {} of type cm:content has been created!", ((NodeResource) repoEvent.getData().getResource()).getName());
}
@Override
public EventFilter getEventFilter() {
return NodeTypeFilter.of("cm:content");
}
}
以下の動作確認も上記と同じパターンなのでソースコードの説明は省略し、どのような操作を行うと、どのクラスからどのようなログが出力されるかだけ紹介します。
コンテンツの更新
先ほど作成した「test.txt」を更新すると、ContentUpdatedHandlerから以下のログが出力されます。
event-api-handlers-sample-1 | 2025-01-23T06:16:24.437Z INFO 1 --- [erContainer#0-1] o.a.s.s.e.handler.ContentUpdatedHandler : The content of the node test.txt has been updated!
属性(cm:title)の更新
「プロパティの編集」から「text.txt」にタイトルを入力して保存すると、TitleModifiedHandlerから以下のログが出力されます。
event-api-handlers-sample-1 | 2025-01-23T06:25:08.965Z INFO 1 --- [erContainer#0-1] o.a.s.s.e.handler.TitleModifiedHandler : The title of the node test.txt has changed from '' to 'テスト'!
ノードの移動
「text.txt」を「共有ファイル」に移動すると、NodeMovedHandlerから以下のログが出力されます。
event-api-handlers-sample-1 | 2025-01-23T06:25:59.353Z INFO 1 --- [erContainer#0-1] o.a.s.s.event.handler.NodeMovedHandler : The node test.txt has been moved from the hierarchy path [7e711768-fb01-4162-b117-68fb0151627d,c5e2311c-96db-4624-a231-1c96db2624ab,6afc954c-7419-4942-bc95-4c74199942d8,30020c87-c65b-4e72-820c-87c65bce7249] to the new hierarchy path [c5e2311c-96db-4624-a231-1c96db2624ab,6afc954c-7419-4942-bc95-4c74199942d8,30020c87-c65b-4e72-820c-87c65bce7249] in the repository!
フォルダの削除
「共有ファイル」配下の「テスト」フォルダを削除すると、FolderDeletedHandlerから以下のログが出力されます。
event-api-handlers-sample-1 | 2025-01-23T06:26:49.516Z INFO 1 --- [erContainer#0-1] o.a.s.s.e.handler.FolderDeletedHandler : The folder named テスト has been deleted!
MIMEタイプが text/html のノードの作成
「共有ファイル」に「test.html」を作成すると、HtmlContentCreatedHandlerから以下のログが出力されます。
event-api-handlers-sample-1 | 2025-01-23T06:27:45.657Z INFO 1 --- [erContainer#0-1] o.a.s.s.e.h.HtmlContentCreatedHandler : An HTML content named test.html has been created!
以上、Out of Process SDKの1つ目のサンプルコードのご紹介でした。まずはサンプルコードを動かしてみていただけると、Out of Process Extensionでどのように開発していくのかイメージが掴みやすくなるのではないかと思います。