操作の定義

以下のセクションでは、すべての操作に適用される一般的なルールについて説明します。

位置の独立性

操作の機能は、フロー内の位置に依存しては​なりません​。

つまり、いずれのコンポーネントも、前後のコンポーネントの副作用に依存する​べき​ではありません。

たとえば、次のフローがあるとします。

<flow name="independence">
  <my-connector:operation-a />
  <my-connector:operation-b />
</flow>

<operation-b>​ は、​<operation-a>​ が先に実行されているかどうかに関係なく、正常に機能する​必要があります​。

非メタデータキーパラメーターが出力データ型を変更しないこと

動作パラメーターによって操作からの戻り値のデータ型が変更されては​なりません​。

パラメーターがメタデータキーとして機能する特殊なケースもあります。このルールは、すべての非メタデータキーパラメーターに適用されます。たとえば、あるモジュールで、特定のアクションを同期的に (出力結果がすぐに返されるように) も、非同期的に (後で結果を取得するために使用できるようにした ID が返されるように) も実行できるようにしたいとします。

これは次のようにして解決できます。

<fruits:get-apple async="false"/>
<fruits:get-apple async="true"/>

最初の操作は apple オブジェクトを返し、2 番目の操作はジョブ ID を表す文字列を返します。

この例は正しくありません。get-apple 操作は常に apple オブジェクトを返す​必要があります​。async パラメーターの設定によって戻り値のデータ型を変更することはできません。

このケースは、2 つの異なる操作としてモデリングする​べきです​。

<fruits:get-apple />
<fruits:get-apple-async />

動的データ型とメタデータキーパラメーター

メタデータキーパラメーターは、操作の出力のデータ型を変更できるため、特殊な扱いをされます。この変更は 2 つのレベルで考えることができます。

  • 操作は常に apple を返すが、apple の定義が使用するシステムインスタンスやユーザーによって異なる (apple の構造が動的でも apple であることは同じである)。

  • 操作が汎用的であり、返されるデータ型はメタデータキーパラメーターによって決定される。

次に例を示します。

<my-connector:search searchType="ORDERS" query="......" />

この操作は、非メタデータキーパラメーターが出力データ型を変更しては​ならない​というルールに違反しません。その理由は次のとおりです。

  • この操作は (リスト内のデータ型が searchType パラメーターに依存するとしても) リストを返します。

  • searchType は ORDERS に設定され、常に ORDERS のリストが返されます。ORDERS ではなく ​ORDER_REFERENCES​ のリストを返すように変更する他のパラメーターは、操作内には存在しない。

操作の出力

操作の出力は、次の条件に準拠している​必要があります​。

常に MIME タイプを渡す

DataWeave で操作の出力を処理するには、常に出力値の MIME タイプが渡されるようにすることが重要です。

SDK は、戻り値のデータ型が Map、POJO、または Java の単純型 (String、Integer、Short など) である場合は、自動的にそのようにしています。

ただし、出力のデータ型が String、InputStream、または byte[] である場合は、モジュール側でその値の MIME タイプを指定する​必要があります​。そうしないと、DataWeave は出力を処理する方法を判断できません。

これには 2 つの方法があります。

  • @MediaType アノテーション

    操作の出力が常に確定している場合は、@MediaType アノテーションを使用する​必要があります​。

    @MediaType("application/json")
    public InputStream getPerson(String personId) {
    // ....
    }
  • 結果オブジェクトの使用

    実行時でなければ戻り値の MIME タイプが分からないケースがあります。たとえば、次のような場合です。

    • ファイルの読み取り (実際の MIME タイプは読み取るファイルによって異なります)

    • 複数の MIME タイプを受け付ける HTTP エンドポイントのヒット (MIME タイプは Content-Type ヘッダーでサーバーから渡されます)

      次に例を示します。

      public Result<InputStream, FileAttributes> read(String path) {
              return Result.<InputStream, FileAttributes>builder()
               .output(readStream(path))
               .mediaType(inferMediaTypeFromFileName(path))
               .attributes(getAttributes(path))
               .build();
      }
  • Java POJO を戻り値のデータ型として使用する

    操作が POJO を返す場合は次のルールが適用されます。

    • クラスをモジュールの API の一部として公開しては​なりません​。

    • オブジェクトは、ロジックを含むことができない値オブジェクトである​必要があります​。

    • オブジェクトには、いかなる静的状態も含まれていては​なりません​。

    • オブジェクトはシリアル化可能である​必要があります​。

  • JSON および XML 出力データ型の処理

    一部の操作は、JSON または XML ドキュメントとして値を返します。

    • Java 型

      JSON および XML ドキュメントは、InputStream の形式で返される​必要があります​。これらのデータ型を表す通常の Java 型 (Reader、Sax オブジェクト、Document、Node、JSON Node など) は使用できません。

    • DataSense による解決

      操作から返されるドキュメントの処理をツールで補助するには、返されるドキュメントのスキーマを知る必要があります。したがって、操作は出力スキーマを明確に定義する​必要があります​。

      • 静的 DataSense

        スキーマが固定されて既知であることがあります。スキーマが既知で静的である場合は、動的な DataSense リゾルバーで問題を解決しては​なりません​。コネクタは、次のいずれかの方法でスキーマを指定する​必要があります​。

        • @OutputJsonType

          モジュールのリソースの一部である JSON スキーマを参照することで出力データ型を定義します。

          @OutputJsonType(schema = "person-schema.json")
          public InputStream getPerson(String personId) {
             // ...
          }
        • @OutputXmlType

          @OutputJsonType ど同様ですが、XML スキーマを参照します。

          @OutputXmlType(schema = "order.xsd", qname = "shiporder")
          public void getOrder(String orderId) {
          ...
          }
        • OutputStaticTypeResolver

          型をプログラムで定義します。

          @OutputResolver(OrderTypeResolver.class)
          public InputStream getOrder(String orderId) {
          ...
          }

動的 DataSense

スキーマが動的である場合は、動的出力リゾルバーが必要です。これを行うには、OutputTypeResolver インターフェースを実装します。

重要なルールは、ユーザーが準拠できる特定のスキーマにモジュールが解決される​必要がある​という点です。動的リゾルバーは、汎用の型である Any を返すことはできません。

@OutputResolver(DynamicOrderTypeResolver.class)
public InputStream getOrder(String orderId) {
...
}

動的 Java 型パラメーター

コネクタが動的なデータ型を処理することは非常に一般的です。たとえば、Salesforce、NetSuite、SAP などのサービスでは、ユーザーがカスタマイズできる基本構造を持つコアエンティティ (Person、Order など) のセットを定義します。

他のサービス (任意の OData サービスなど) では、さらに複雑で完全なカスタムデータ型セットを定義できます。

これらの動的データ型を返すすべての操作には、現在の設定で使用する実際の型定義を提供する @OutputTypeResolver が関連付けられている​必要があります​。

これについては、JSON や XML ドキュメントとして返される出力値のセクションで取り上げました。Map やカスタム POJO などの Java 型として表されるパラメーターにも同じルールが適用されます。どの Java クラスを使用して表現されるかには関係なく、他のすべての動的データ型に対しては、慎重に定義された複雑な OutputTypeResolver が存在する​必要があります​。

ストリーミング

byte[] ではなく InputStream を使用する

byte[] 配列ではなく InputStream を戻り値のデータ型として使用する​べきです​。バイト配列のサイズが小さい場合は、バイト配列を返しても構いません。

配列のサイズが不明、可変、または大きくなる可能性がある場合には、操作は InputStream を返す​必要があります​。

これにより、大量のメモリ消費を抑えて、特に小さいワーカーでの実行時に OutOfMemoryError の発生を防止できます。InputStream を返すことで、大量の情報をすべてメモリに読み込むことを避ける必要があることを示すことができます。byte[] 配列全体をメモリに読み込んでそのまま ByteArrayInputStream に完全にラップするという処理は、このルールの目的を損なうため、避ける​必要があります​。

大きな文字列を返さない

サイズが 4 KB を超えない限り、文字列を返すことには問題ありません。それを超える可能性がある場合は、操作は文字列ではなく InputStream を返し、@OutputResolver を使用して予想されるメディア種別のメタデータを提供する​必要があります​。

StreamingHelper を使用する

コンポーネントがストリーム可能なリソース (InputStream や PagingProvider) を返す場合、SDK は (アプリケーションがそのように設定されていれば) これらのストリームを自動的に反復可能にします。

これらのリソースがマップなどの上位構造に含まれている場合は、​ StreamingHelper​ を使用して、SDK に対してその適合処理を実行するように明示的に指示する​必要があります​。

よくあるケース:

  • ストリームがマップに含まれる

    このケースの例としては、データベースから取得した結果セットが、キーを列名、値を実際の結果としたマップの形式で返される <db:select> 操作があります。結果セットに BLOB 列が含まれている場合、その値は InputStream 型となります。この場合、コネクタは次のメソッドを使用する​必要があります​。このメソッドは、すべてのストリーム可能リソースが反復可能に設定された新しいマップを返します。

    org.mule.runtime.extension.api.runtime.streaming.StreamingHelper#resolveCursors(Map, boolean)

  • ストリームが POJO に含まれる

    データベースのケースには当てはまりませんが、InputStream が実際には POJO の項目に含まれるユースケースも考えられます。

    上のケースでは、応答の実際の構造がクエリごとに変わるので不明であるため、​<db:select>​ はマップを返します。トラブルシューティングが難しくなるような問題が発生しないように、事前によく計画してください。

    ただし、POJO を使用する理由は、構造が固定されていて、通常の POJO には既知の属性とストリーム可能な情報が含まれるからです。100% そうであるという保証はできませんが、通常は固定されている POJO の属性を Attributes オブジェクトに渡してペイロードにストリーミングすることで、標準ケースとする​べきです​。

    極めてまれなケースですが、そうすることが妥当であれば、ストリーミングリソースを含む項目を、org.mule.runtime.extension.api.runtime.streaming.StreamingHelper#resolveCursorProvider(Object) メソッドを使用して適合させる​必要があります​。

    ストリーム可能リソースを含む POJO のパターンは明確に禁止されてはいませんが、コードの肥大化を招くことがあるため、慎重にレビュー​すべき​であり、カスタムメタデータリゾルバーと調和する​必要があります​。

    このシナリオが有効な場合には、値が CursorProvider に適合されるかどうかは不明であるため、カスタムメタデータリゾルバーが必要です。Object 型である必要があり、カスタムリゾルバーがなければ DataSense は型を特定できません。

非ブロック I/O を使用する

Mule 4 は、非ブロック I/O 操作を実行するように最適化されたリアクティブ実行エンジンです。

入力または出力操作を非ブロックモードで実行する場合は、コネクタは MuleSoft の​非ブロック操作​要件に準拠する​必要があります​。

非ブロック I/O の使用が最適なケース:

  • ローカルファイルシステムでのファイルへのアクセス

  • HTTP 要求

  • TCP/UDP ソケット要求

  • 非ブロッククライアントが存在する I/O 操作

非ブロック I/O を使用しないケース:

  • JDBC 経由でのデータベースへのアクセス (非ブロック JDBC ドライバーはまだ実験段階です)

  • 非ブロックの同等オプションが存在しないか、またはその信頼性が低いクライアントを使用する場合 (たとえば、Apache Commons Net を使用した FTP サーバーへのアクセスでは、信頼性の高い非ブロックオプションは存在しません)

非ブロックと非同期は同じではない

非同期と非ブロックが混同されることがよくあります。これらは同じではありません。この非ブロック I/O を優先するというルールは、すべての I/O 操作を、非同期的に実行される別々のスレッドにディスパッチ​すべき​という意味ではありません。

非ブロック I/O の仕組みと、Mule でどのように実装されているかについてよく理解しておいてください。

情報:

トランザクションを使用する

操作がトランザクションシステム (XA トランザクションを含む) とやり取りする場合は、その機能をサポートする​必要があります​。「トランザクション」を参照してください。