日別アーカイブ: 2019年6月21日

Logic Apps と Cosmos DB

このエントリは2019/06/21現在の情報に基づきます。そのため、将来の機能追加・廃止に伴い、記載内容との乖離が発生する可能性があります。

関連エントリ

Serverlessというコンセプトに則ったサービスであるLogic Appsは、複雑なロジックは向いていないながらも、シンプルなロジックであれば、コードをできるだけ書かずにFunctionsのような動作をさせることができる。今回はCosmos DBに対する操作を確認した。なお、データはAPI Managementのエントリで使っている3レターコードを格納したコレクションを使っている。

コネクタ

Azure Cosmos DBのコネクタが存在するが、アクションとしてのみ利用可能で、トリガーとして利用することはできない。変更フィードをLogic Appで取り扱うためには、現時点では以下のように、Cosmos DBとLogic Appの間mediationするものを挟む必要がある。

  • Functionsで受け取ってmediationするものを作成、もしくは連携する(Event GridやEvent Hub、Service Busなど)
  • 変更フィードプロセッサライブラリを使ってmediationするものを作成、もしくは連携する(ただし.NETのみ)
  • Azure Cosmos DB SQL API SDK を使用してmediationするものを作成、もしくは連携する

クエリ

[1つのドキュメントを取得する]アクションを使う場合、[ドキュメントID]はダブルクォーテーションで囲まない点に注意が必要。対して、[パーティション キーの値]は、必ずダブルクォーテーションで囲む必要がある。

SFOを指定した場合、以下のような結果が返る(一部マスク済み)。当然ながら、この内容はCosmos DBのデータエクスプローラーで確認できる値と同じ(ただし_tsを除く)。

{
    "code": "SFO",
    "id": "SFO",
    "en": "San Francisco, CA",
    "ja": "サンフランシスコ",
    "_rid": "...",
    "_self": "dbs/.../",
    "_etag": "...",
    "_attachments": "attachments/",
    "_ts": 1561101814
}

もし複数件のレコードを取得する可能性がある場合、[複数のドキュメントにクエリを実行する]を使うとクエリを記載できる。

この場合、codeとしてTを指定すると、以下のように複数のレコードが返る。

[
    {
        "code": "CTS",
        "en": "Sapporo (Chitose)",
        "ja": "札幌(千歳)"
    },
    {
        "code": "TTJ",
        "en": "Tottori",
        "ja": "鳥取"
    },
    {
        "code": "AXT",
        "en": "Akita",
        "ja": "秋田"
    },
    {
        "code": "NTQ",
        "en": "Noto",
        "ja": "能登"
    },
    {
        "code": "TOY",
        "en": "Toyama",
        "ja": "富山"
    },
    {
        "code": "TAK",
        "en": "Takamatsu",
        "ja": "高松"
    },
    {
        "code": "OIT",
        "en": "Oita",
        "ja": "大分"
    },
    {
        "code": "TYO",
        "en": "Tokyo (All)",
        "ja": "東京(すべて)"
    },
    {
        "code": "ITM",
        "en": "Osaka (Itami)",
        "ja": "大阪(伊丹)"
    },
    {
        "code": "NRT",
        "en": "Tokyo (Narita)",
        "ja": "東京(成田)"
    },
    {
        "code": "TKS",
        "en": "Tokushima",
        "ja": "徳島"
    },
    {
        "code": "TSJ",
        "en": "Tsushima",
        "ja": "対馬"
    }
]

挿入・更新

挿入・更新時には、Upsertするか否かを指定できる(デフォルトでは出てこないので、[新しいパラメーターの追加]から選択する必要がある)。また、[パーティション キーの値]は、必ずダブルクォーテーションで囲むこと。

削除

これも[1つのドキュメントを取得する]アクションと同様、[ドキュメントID]はダブルクォーテーションで囲まない点に注意が必要。対して、[パーティション キーの値]は、必ずダブルクォーテーションで囲む必要がある。

まとめ

設定フィールドごとにダブルクォーテーションで囲む・囲まないの設定が必要なので、注意が必要である。

Microprofile CustomConfigSource with Database

このエントリは以下のエントリをベースにしています。
This entry is based on the following one written by Ralph Soika (Project lead of Imixs-Workflow).
https://microprofile.io/2019/06/18/microprofile-customconfigsource-with-ejbs/
https://ralph.blog.imixs.com/2019/06/11/microprofile-customconfigsource-with-database/

MicroProfile Config APIでは、アプリケーションの構成プロパティを簡単かつ新しい方法で扱うことができます。

Configuration for MicroProfile
https://microprofile.io/project/eclipse/microprofile-config

このAPIを使うと、以下のような異なるソースの構成やプロパティの値にアクセスできます。

  • System.getProperties() (ordinal=400)
  • System.getenv() (ordinal=300)
  • すべての META-INF/microprofile-config.properties ファイル

MicroProfile Config APIの手始めには以下をどうぞ。

Eclipse MicroProfile Config – what is it?
https://www.eclipse.org/community/eclipse_newsletter/2017/september/article3.php

もちろん、ご自身のConfigSourceも実装できますが、大多数は以下の例のように既存ファイルからのカスタム構成値の読み取りをベースにしています。

Microprofile Config: Creating a Custom ConfigSource
https://rhuanrocha.net/2018/12/21/microprofile-config-creating-a-custom-configsource/

このエントリではデータベースから読み取った値に基づいたMicroProfile ConfigSourceの実装方法をご紹介します。

How To Access a Database?

以下の例は、JPAデータソースもしくはEJBサービスから値を読み取るカスタムのConfigSourceを実装方法を示したものです。一見すると、アプリケーションが提供するカスタム構成ファイルへアクセスするための外部ソースやサービスを注入するのは簡単そうに見えます。

public class MyConfigSource implements ConfigSource {

    @PersistenceContext(unitName = ".....")
    private EntityManager manager;

    @Override
    public String getValue(String key) {
       .....
    }
    @Override
    public Map<String, String&gt; getProperties() {
       // read data form JPA Entity manager
       ....
    }
}

しかしながら、この直接的なやり方には問題があります。JPA Entity Managerや単なる別のEJBをCustomConfigSourceに注入しようとすると、このEntity ManagerがNullのため、値が想定通りに利用できないことに気づくでしょう。

この理由は、MicroProfile ConfigではすべてのConfigSourcesをPOJOとして取り扱うためです。そのため、注入された値は利用できません。例えば別のCDI beanが起動時に構成値が注入されることを想定していると考えてください。カスタムのConfigSource自体がCDIに依存していると、起動ループの問題が発生する可能性があります。その場合、どうすればよいでしょうか。

解決策は、Java Enterpriseではよくあることですが、非常にシンプルです。EntityManagerが注入済みであることを確実にするため、カスタムConfigSourceに@Startupを付け、@PostConstructをつけたメソッドを実装すればよいのです。

@Startup
@Singleton
public class MyConfigSource implements ConfigSource {
    @PersistenceContext(unitName = ".....")
    private EntityManager manager;

    @PostConstruct
    void init() {
        // load your data from teh JPA source or EJB
        ....
    }
   ...
}

こうすれば、init()メソッドで、自身で用意したEntity Manager(もしくは注入されたものすべて)にアクセスできます。MicroProfile Config APIは構成ソースをPOJOと引き続き見なしているため、作成したクラスを2度生成します(1回目はConfig APIから、2回目は@PostConstructのCDI実装からそれぞれコンストラクタが呼び出される)。では、両インスタンスに値を設定するにはどうすればよいのでしょうか。

こちらも解決策は極めてシンプルです。ConfigSourceはPOJOなので、静的メンバー値を使ってあたいを格納できます。この方法で、カスタムConfigSourceの各インスタンスで同一の値を確認できるのです。@PostConstructをつけると、ある種の遅延ロードで構成の値を設定します。以下の例をご覧ください。

@Startup
@Singleton
public class MyConfigSource implements ConfigSource {

    public static final String NAME = "MyConfigSource";
    public static Map<String, String&gt; properties = null; // note to use static here!

    @PersistenceContext(unitName = ".....")
    private EntityManager manager;

    @PostConstruct
    void init() {
        // load your data from teh JPA source or EJB
        ....
        // override the static property map..
        properties.put(....)
    }

    @Override
    public int getOrdinal() {
        return 890;
    }

    @Override
    public String getValue(String key) {
        if (properties != null) {
            return properties.get(key);
        } else {
            return null;
        }
    }

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public Map<String, String&gt; getProperties() {
        return properties;
    }
}

静的メンバー値であるpropertiesを使い、すでに生成済みのConfigSourceからの値をオーバーロードします。これにより、ConfigSourceの任意のインスタンスは同じ値を共有します。値は後でロードされるため、ConfigSourceで起動時に値は設定されません。これはつまり別のCDI beanがあれば、@PostConstructの愛大これらの値にアクセスできない、ということです。しかしながら、値は実行時には利用できるようになっています。

遅延ロードメカニズムの不利な点があるため、この解決策は実装が極めてシンプルかつ簡単です。もちろんJNDI Lookupを使って、遅延ロードのトリックを使わずにデータソースからデータを取得することもできます。ここで紹介した解決策を使うと、データソースだけでなく、任意のCDIにアクセスできます。オープンソースプロジェクトlmixs-Workflowの最新版はMicroProfile 2.2ベースですが、この中でこの解決策を使っています。

Imixs Workflow – Open Source Workflow Engine for Human-Centric BPM
https://www.imixs.org/