Thursday, March 28, 2019

Liferay DXP 7.1でシンプルにサーバサイドロジックを追加する

こんにちは。おおたにです。

今回は、Liferay DXP 7.1にサーバサイドロジックを追加する方法を紹介します(7.0でも使えます)。本記事は、主に以下のような要件にマッチします。

  • Web APIとしてロジックを公開する
  • Liferayとは直接関わりのない機能を実装する(認証や各種サービスなどLiferayが提供する機能は使わない)

Liferayが提供するサービスを使った実装を行うのであればサービスビルダを使ってリモートサービスを実装し、そうでなければJAX-RSを使って実装するのが定石かと思うのですが、ここではOSGi HTTP Whiteboardを使った実装を紹介します。
(もちろん、Liferayと完全に疎結合なロジックであれば、Liferayの前面に構築されたWebサーバのレイヤでCGI等を使って実装するのもありですが、ここではそれには触れません)

OSGi HTTP Whiteboardとは


OSGi HTTP WhiteboardとはOSGi Compendiumで規定されている仕様の1つで、サーブレットやサーブレットフィルタ、HTML/JS/画像等の静的リソースなどをOSGiバンドルを使って公開するためのシンプルな仕組みを提供します。Liferay DXPはOSGiの基盤の上に実装されているため、そのカスタマイズにOSGiの仕様を利用しない手はありません。具体的には、以下の2ステップでWeb APIを公開できます。シンプル!

  • Servlet等をコンポーネントとして実装したOSGiバンドルを開発する
  • 開発したOSGiバンドルをLiferay(OSGi実行基盤)にデプロイする

この方法は素のServletを直接公開するようなものなので、認証等でLiferayの仕組みを利用したい、もしくは関連する複数のAPIをまとめて実装/公開したい場合はJAX-RSを使い、Liferayの仕組を全く使用せずなるべく疎結合に、ちょっとしたロジックを実装したい場合はHTTP Whiteboardを使うというような使い分けが考えられます。

実装してみよう


では早速実装してみましょう。まずはServletコンポーネントを作成します。実装のポイントは以下の4点です。

  • HttpServlet の実装クラスを作成する
  • @Component アノテーションでコンポーネント宣言する
  • osgi.http.whiteboard.servlet.name でServlet実装の完全修飾クラス名を指定する
  • osgi.http.whiteboard.servlet.pattern でServletをマップするURLパスを指定する

例えば以下のように実装します。
package jp.aegif.liferay.sample;
 
@Component(
  immediate = true,
  property = {
    "osgi.http.whiteboard.servlet.name=jp.aegif.liferay.sample.SampleWhiteboardServlet",
    "osgi.http.whiteboard.servlet.pattern=/get-email-address-hash" // Web APIのパスを設定する
  },
  service = Servlet.class
)
public class SampleWhiteboardServlet extends HttpServlet {
  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
    // 必要な処理とレスポンスを記述する
    // sample implementation to generate SHA-1 hash of Munchkin API Private Key + Email Address
    response.setContentType("text/plain;charset=UTF-8");
    PrintWriter printWriter = response.getWriter();
    try {
      String source = API_PRIVATE_KEY + Objects.toString(request.getParameter("email"), "");
      byte[] bytes = MessageDigest.getInstance("SHA-1").digest(source.getBytes(StandardCharsets.UTF_8));
      printWriter.print(DatatypeConverter.printHexBinary(bytes));
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }
    printWriter.close();
  }

  private static final String API_PRIVATE_KEY = "change_it"; // Set your Munchkin API Private Key
}

このサンプルは、SaaSのマーケティングオートメーションツールMarketoassociateLead API向けハッシュ生成ロジックを実装してみたものです。Liferayのフォームで入力した情報を使ってリード登録やcookie紐づけを行いたい場合に利用できるかと思います。associateLead APIのコールには、MarketoのAPIプライベートキーを利用して生成したハッシュが必要なため、サーバサイドロジックとして実装する必要があります。なお、このロジックをクライアントサイドのJavaScriptで実装すると、APIプライベートキーが全世界に向けて公開されてしまいますのでくれぐれもご注意を…。

あとはOSGiバンドル作成のためのbnd.bndやbuild.gradle等のビルド用ファイルを用意してjarファイルをビルドするだけです。bndtoolsやgradleを個別にセットアップしてビルドすることも可能ですが、Liferay Developer StudioやLiferay IDEを使うと簡単にビルドできます。

デプロイして試してみよう


ビルドが成功したら、早速デプロイしてみましょう。

  1. <LIFERAY_HOME>/deploy フォルダにjarファイルをコピーする
  2. ログに STARTED jp.aegif.liferay.sample.whiteboard.servlet_1.0.0 と表示される

以上でデプロイ完了です。ホットデプロイされるため、Liferayを再起動することなくコンポーネントの機能が有効化されるはずです。

動作確認のために http://localhost:8080/o/get-email-address-hash?email=test@example.com にアクセスしてみましょう。ハッシュコードが返ってくればOKです。LiferayではOSGi HTTPエンドポイントパスが /o/ となっているため、Web APIのURLは http://<サーバ名>:<ポート番号>/o/<ServletをマップしたURLパス> となります。リクエストパラメータ email でメールアドレスを指定します。


今回の内容は以上です。みなさんもHTTP Whiteboardを使ってシンプルにWeb APIを公開してみてください!