4

JavaLoader を使用して、CF によって呼び出された Web サービスによって返されたオブジェクトを取得し、JavaLoader によって読み込まれたオブジェクトを同じクラスパス コンテキストにすることは可能ですか? というか、さほど苦労せずに?

// get a web service
ws = createObject("webservice", local.lms.wsurl);
// user created by coldfusion
user = ws.GenerateUserObject();
/* user status created by java loader.
** this api provider requires that you move the stubs
** (generated when hitting the wsdl from CF for the first time)
** to the classpath.
** this is one of the stubs/classes that gets called from that.
*/
UserStatus = javaLoader.create("com.geolearning.geonext.webservices.Status");
// set user status: classpath context clash
user.setStatus(UserStatus.Active);

エラー:

  • 詳細: 指定されたメソッド名と引数の型を持つメソッドがないか、ColdFusion が確実に解読できない引数の型で setStatus メソッドがオーバーロードされています。指定された引数に一致するメソッドが 0 個見つかりました。これが Java オブジェクトであり、メソッドが存在することを確認した場合は、javacast 関数を使用してあいまいさを減らします。
  • メッセージ: setStatus メソッドが見つかりませんでした。
  • MethodName setStatus

呼び出しは表面的にはユーザーのメソッド シグネチャと一致しますが、setStatus(com.geolearning.geonext.webservices.Status) ですが、クラスは別のクラスパス コンテキストにあります。そのため、上記のエラーが発生します。

4

1 に答える 1

5

Jamie と私はこのオフラインで作業し、創造的な解決策を思いつきました :)

(長い回答で申し訳ありませんが、クラスローダーが私と同じように混乱していると思う人には、少し説明が必要だと思いました。「理由」の側面に興味がない場合は、気軽に最後までジャンプしてください)。

問題:

この問題は、複数のクラス ローダー/パスが原因であることは間違いありません。どうやら CF Web サービスは動的 URLClassLoader (JavaLoader と同様) を使用します。これにより、生成された Web サービス クラスがコア CF の「クラス パス」になくても、オンザフライでロードできます。

(私の限られた理解に基づいて...)クラスローダーは階層に従います。複数のクラスローダーが関与する場合、特定のルールを守らなければなりません。そうしないと、うまく連携できません。ルールの 1 つは、子クラス ローダーは祖先 (親、祖父母など) によってロードされたオブジェクトのみを「見る」ことができるというものです。兄弟によってロードされたクラスを見ることはできません。

JavaLoader によって作成されたオブジェクトと によって作成されたオブジェクトを調べるとcreateObject、それらは確かに兄弟、つまり CF ブートストラップ クラス ローダーの両方の子です。そのため、一方が他方によってロードされたオブジェクトを認識しないため、setStatus呼び出しが失敗した理由が説明されます。 2 つのオブジェクトの子と親のクラス ローダー

親によってロードされたオブジェクトを子見ることができる場合、明らかな解決策は、オブジェクトの構築方法を変更することです。クラスローダーの 1 つが他のクラスローダーの親になるように、呼び出しを構成します。不思議なことに、それは思ったよりもトリッキーであることが判明しました。いくつかの組み合わせを試しましたが ( switchThreadContextClassLoaderメソッドの使用を含む)、それを実現する方法を見つけることができませんでした。

解決:

最後に、私はクレイジーな考えを持っていました.jarをロードしないでください。Web サービスのローダーをparentClassLoader. 独自の個別の「クラスパス」に必要なものがすべて含まれています。

    // display class path of web service class loader
    dynamicLoader = webService.getClass().getClassLoader();
    dynamicClassPath = dynamicLoader.getURLS();
    WriteDump("CLASS PATH: "& dynamicClassPath[1].toString() );

JavaLoader は、見つからないクラスの呼び出しを自動的に委任しますparentClassLoader。そしてビンゴです。すべてが機能します。クラスローダーの競合はもうありません。

    webService = createObject("webservice", webserviceURL, webserviceArgs);
    javaLoader = createObject("component", "javaloader.JavaLoader").init(
            loadPaths = [] // nothing
            , parentClassLoader=webService.getClass().getClassLoader()
        );

    user = webService.GenerateUserObject();
    userStatus = javaLoader.create("com.geolearning.geonext.webservices.Status");
    user.setStatus(userStatus.Active);
    WriteDump(var=user.getStatus(), label="SUCCESS: user.getStatus()");
于 2013-03-23T21:10:08.760 に答える