Friday, August 28, 2020

gogo shellコマンドを作ってみよう

 とたにです。

今回は実は色々な用途に使えるgogo shellコマンドの作り方を紹介したします。

※本記事はLiferay DXP 7.1を使用していますがバージョン7.0〜7.2でも(おそらく7.3も)問題なく動くと思います。

そもそもgogo shellとは

gogo shellコマンドの前にgogo shellとはなんでしょうか。LiferayはDXP(7.0)以降、アーキテクチャがOSGiベースに刷新されました。gogo shellはそのOSGiランタイムのコマンドラインシェルのことで、正しくはApache felix gogo shellといいます。
Liferayにも、このgogo shellを組み込まれているためgogo shellにアクセスしてさまざまな情報を取得したりOSGiバンドルの操作をしたりすることができます。

gogo shellへの接続方法

gogo shellへの接続方法は大きく2通りあります。
1つ目はLiferayのコントロールパネルからgogo shellにアクセスする方法です。
コントロールパネルの設定からGogo shellをクリックすると以下のようなGogo shellコマンドをWebページから入力できます。


2つ目は、telnetを使って接続する方法です。ただし7.1以降でtelnetを使って接続するためには以下の設定をportal-ext.propertiesに設定しておく必要があります。

 module.framework.properties.osgi.console=localhost:11311

この状態でLiferayを起動すると以下のようにtelnetからgogo shellに接続することができます。

$ telnet localhost 11311
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
____________________________
Welcome to Apache Felix Gogo

gogo shellではさまざまなコマンドが標準で用意され、主にOSGiバンドルの状態確認や調査、状態変更などが行えます。helpコマンドで使用できるコマンドの一覧を確認することができるので興味のある方は確認してみてください。

gogo shellコマンドの作成方法

gogo shellでは用意されたコマンドを使うだけではなく新しくコマンドを追加することができます。
今回は非常に簡単なコマンドを作成してみたいと思います。

step.1 Liferayモジュールプロジェクトの準備

Liferay IDEやbladeコマンドでLiferay workspaceとコマンドを追加するためのモジュールプロジェクトを作成します。プロジェクトの種類はシンプルでよいのでactivator等を選んでおくのが簡単かと思います。

今回はLiferayのサイト一覧とサイトメンバの一覧をコマンドで表示するコマンドを作ろうと思っているので、Liferayのサービスが呼び出せるようにbuild.gradleを以下のようにしておきます。
dependencies {
	compileOnly group: "org.osgi", name: "org.osgi.core"
	compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations"
	compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel"	
}

step.2 コマンドクラスの作成

package sample;

import com.liferay.portal.kernel.dao.orm.QueryUtil;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.model.Group;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.service.GroupLocalService;
import com.liferay.portal.kernel.service.UserLocalService;
import com.liferay.portal.kernel.util.PortalUtil;

import java.util.List;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;



@Component(
        property = {
                "osgi.command.function=listSites",
                "osgi.command.function=siteMembers",
                "osgi.command.scope=sample"
        },
        service = Object.class
)
public class SIteMemberCommand {

    public void listSites() {
    	List>group> sites = this.groupLocalService.getGroups(QueryUtil.ALL_POS, QueryUtil.ALL_POS);
    	
    	sites.stream().forEach(g -> System.out.println(g.getFriendlyURL()));
    	System.out.println("-- Total: " + sites.size());
    }
    
    public void siteMembers(String siteUrl) throws PortalException {
    	
    	Group g = this.groupLocalService.getFriendlyURLGroup(PortalUtil.getDefaultCompanyId(), siteUrl);
    	List>user> users = this.userLocalService.getGroupUsers(g.getGroupId());
    	
    	users.stream().forEach(u -> System.out.println(u.getScreenName() + ", " + u.getEmailAddress()));
    }
    
    @Reference
    private GroupLocalService groupLocalService;
    
    @Reference
    private UserLocalService userLocalService;
}
大した長さではないので、クラスのコードを全部はっています。

一番大切な部分は、クラスに付与されたアノテーションで、osgi.comand.functionとosgi.comnand.scopeプロパティの部分です。
osgi.command.functionはコマンドの名前と対応するメソッド名を定義しています。今回の例だとlistSitesと入力するとこのクラスのlsitSitesメソッドが実行されるようになります。
またosgi.command.functionは複数行記述することで複数のコマンドをひとつのクラスにまとめて定義することができます。

osgi.command.scopeはコマンドの名前空間でコマンド実行時には省略することもできますが、コマンド名が衝突しているケースで[スコープ名]:[コマンド名]のように入力することでコマンドを限定することができます。

コマンドの中身はLiferayの標準サービスを使って簡単な情報を出力しているだけなので詳細は省略しますが、コマンドを実行すると以下のように動きます。

  • listSitesコマンドはLiferay上のサイトのフレンドリURL一覧を出力します。
  • siteMembersコマンドはフレンドリURLを引数にとって、そのサイトの所属ユーザ一覧を出力します。

step.3 結果確認

モジュールをビルドしてLiferayにデプロイしたらコマンドの動作を確認してみましょう。

g! listSites  
/landing1
/landing2
...
/uitest
/personal_site
/template-35187
/template-35237
/template-33896
/template-20154
/template-33875
/template-33851
/template-33862
-- Total: 115

サイトテンプレートなどもサイトのひとつとして表示されていますが、いちおうサイトの一覧は取得できているようです。
次にsiteMembersもためしてみましょう。

g! siteMembers /contacttest
test, test@liferay.com

サイトメンバが1人しかいないですが、サイトのメンバーが表示されていることがわかります。

まとめ

Liferayのサービスと連携したgogo shellコマンドを作成しました。
gogo shellコマンドはこのようにLiferayのサービスやサービスビルダで作成したサービスを呼び出すことができるので、例えば初期データの登録やサービスレイヤーのテストなどさまざまな用途に活用することができます。
作成方法も簡単なのでぜひ活用してみてください。

No comments: