クラスローディング分離について

Mule 4 のクラスローディングスキーマは、ランタイム、アプリケーション、モジュールを互いに分離します。それぞれは (クラスではなく) パッケージを指定し、API の一部としてエクスポートして、これらのパッケージを可視化します。

Mule 3 のクラスローディングの問題:

  • モジュールが互いに分離されなかった。

  • モジュールが Mule Runtime から分離されなかった。

  • モジュールが Mule アプリケーションから分離されなかった。

Mule 3 では、一部のライブラリを使用するモジュールが、同じライブラリの別のバージョンを使用する他のモジュール、アプリケーション、またはランタイムと競合することがありました。この競合によってクラスローディングの問題が発生し、​ClassNotFoundException​ や ​NoSuchMethodException​ など、トラブルシューティングの困難な混乱を招くエラーメッセージが表示されることがありました。

モジュールのクラスローディング分離

クラスローディングは、クラスではなくパッケージによって自動的に絞り込まれるため、どのパッケージをエクスポートして Mule アプリケーションに対して可視化するかを、モジュールが指定する必要があります。

エクスポートするパッケージを判断するために、SDK は各設定、接続、コンポーネントの戻り値と入力種別を調べてそれらのパッケージを取得します。そのため、​エクスポートしない​クラスがエクスポートされるパッケージから除外されていることを確認する必要があります。エクスポートされるクラスは、エクスポートしないクラスとパッケージを共有していない必要があります。

次のパッケージ階層をお勧めします (MuleSoft モジュールで内部的に使用)。

org.mule.module
\--> .api.*
\--> .internal.*
  • .api.*​ には、Mule アプリケーションによってエクスポートされ表示されるすべてのクラス (戻り値のデータ型、サブタイプ、入力種別、接続インターフェース) が含まれる。

  • .internal.*​ には、モジュールの特定の動作に使用されるすべてのクラス (アノテーション付きのクラス、​Connection​ および ​Operation​ 実装と内部クラス) が含まれる。

エクスポートされたパッケージと戻り値のデータ型の把握

MuleSoft では、エクスポートされた​​すべての​​クラスがモジュールに属することを推奨しています。

ライブラリの衝突により、モジュールと他のモジュール、アプリケーション連動関係、さらにはランタイムとの互換性がなくなるという問題を回避するため、以下が​強く推奨​されます。

  • モジュールが使用しているライブラリに属するクラスからは​絶対に​​オブジェクトを戻したり受け取ったりしないこと。

次のセクションのシナリオを検討してください。

1 つ目のシナリオ: 同じパッケージをエクスポートする

このシナリオでは、操作が非常に異なる 2 つのモジュールを使用します。どちらも同じライブラリ (GSON) を使用して JSON を解析し、​JsonObject​ クラスのインスタンスを​返します​。片方は GSON バージョン ​1.1​、他方は GSON バージョン ​2.3​ を使用します。​JsonObject​ クラスは、両方のバージョンとして同じパッケージに含まれていますが、各バージョンの実装が異なるため、クラス間の互換性がなくなります。

この例では、​JsonObject​ クラスを操作の戻り値のデータ型として公開しています。

最初のモジュール操作
public JsonObject getEmployee(@Connection EmployeesServiceConnection connection, String id) {
  return new JsonObject(connection.getById(id));
}
2 番目のモジュール操作
public JsonObject getAllProducts(@Connection ProductsServiceConnection connection) {
  return new JsonObject(connection.getProdsJson());
}

Mule アプリケーションをデプロイしてから、同じパッケージをエクスポートする 2 つのモジュールを使用しようとすると、2 つのモジュールが同じパッケージをエクスポートできないため、Mule はエラーとなります。

2 つ目のシナリオ: 同じクラスの異なるバージョンの 2 つのインスタンスを戻す

前述の例を少し変えて、操作が ​GSON​ パッケージをエクスポートしないものとします。その代わりに ​java.lang.Object​ を戻します。

最初のモジュール操作
public Object getEmployee() {
  // Here JsonObject is from GSON 1.1
  return new JsonObject();
}
2 番目のモジュール操作
public Object getAllProducts() {
  // Here JsonObject is from GSON 2.3
  return new JsonObject();
}

アプリケーションで両方のモジュールを使用すると、両方のクラスの完全修飾名がまったく同じであるため、2 つのバージョンのうち片方だけが読み込まれます。完全修飾名の衝突によって、パッケージの​読み込まれなかったほうの​バージョンを必要とするモジュールは、異なる動作を起こして ​ClassCastException​ や ​NoSuchMethodException​ などのエラーが発生しやすくなります。

@Export アノテーション

SDK によって自動的にエクスポートされないクラスをモジュールがエクスポートする必要があるという、極めて厄介なケースや複雑なケースでは、​@Export​ アノテーションを使用できます。

@Export​ アノテーションは、エクスポートするクラスの配列を受け取ります。

同じパッケージのすべてのクラスをエクスポートクラスとしてエクスポートしない限り、特定のクラスだけをエクスポートすることはできません。