9

私の osgi アプリケーションには、 と の 3 つのバンドルがtravel.apiありtable.apiますutilstravel.apiに依存table.apiutilsます。travel.apiに直接依存しないことに注意してくださいutils。aQute Bnd を使用してマニフェストを生成していますが、正常に動作していると思います。マニフェストを以下に示します。

PageDatatype のフィールドを持つと呼ばれるクラスがありTableData、これにはさらに type のフィールドがありTestObjectます。PageDataは にありtravel.apiTableDataは にありtable.apiTestObjectは にありutilsます。バンドルが読み込まれると、これはすべて正常に機能します。問題は、オブジェクトを表すバイト配列を受け取ったときに発生しPageDataます。travel.apiバンドルでデシリアライズする必要があります。それが定義されている場所であるため、これは問題にはなりません。バンドルからクラスローダーを使用org.jboss.netty.handler.codec.serialization.ObjectDecoderInputStreamして渡します。travel.api以下に示す例外がスローされますが、基本的には次のようになります。

Caused by: java.lang.ClassNotFoundException: com.openaf.utils.TestObject not 
    found by travel.api [9].

Import-Packagefortravel.apiを見るとcom.openaf.utils(場所がどこにあるか) がリストされていないことがわかるので、これは理にかなってTestObjectいます。このパッケージを追加すると、正しく逆シリアル化されます。PageDataただし、使用するすべてのフィールドを調べて、それらがすべてこのモジュールにインポートされていることを確認する必要があるため、これらのフィールドに含まれるすべてのフィールドで再帰的に行う必要があるため、これは一般的な解決策とは思えません。

私はここで何か完全に間違っていますか?

OSGi を使用するときにオブジェクトをデシリアライズする最良の方法は何ですか?

私がそれを正しく行っていて、すべての「深い」インポートを指定する必要がある場合、Bnd に「深い」生成をさせる方法はありますか?

どんな助けでも大歓迎です!

osgi ライブラリとして felix v4 を使用しています。

Manifest-Version: 1
Bnd-LastModified: 1355404320862
Bundle-ManifestVersion: 2
Bundle-Name: travel.api
Bundle-SymbolicName: travel.api
Bundle-Version: 0
Created-By: 1.7.0_07 (Oracle Corporation)
Export-Package: com.openaf.travel.api;uses:="scala.runtime,scala,scala.c
 ollection,com.openaf.pagemanager.api,scala.reflect,com.openaf.table.api
 ";version="0.0.0"
Import-Package: com.openaf.pagemanager.api,com.openaf.table.api,scala,sc
 ala.collection,scala.reflect,scala.runtime
Tool: Bnd-1.44.0

Manifest-Version: 1
Bnd-LastModified: 1355404158858
Bundle-ManifestVersion: 2
Bundle-Name: table.api
Bundle-SymbolicName: table.api
Bundle-Version: 0
Created-By: 1.7.0_07 (Oracle Corporation)
Export-Package: com.openaf.table.api;uses:="scala.runtime,scala,scala.co
 llection,scala.reflect,scala.collection.immutable,scala.collection.gene
 ric,com.openaf.utils";version="0.0.0"
Import-Package: com.openaf.utils,scala,scala.collection,scala.collection
 .generic,scala.collection.immutable,scala.reflect,scala.runtime
Tool: Bnd-1.44.0

Manifest-Version: 1
Bnd-LastModified: 1355404158801
Bundle-ManifestVersion: 2
Bundle-Name: utils
Bundle-SymbolicName: utils
Bundle-Version: 0
Created-By: 1.7.0_07 (Oracle Corporation)
Export-Package: com.openaf.utils;uses:="scala.runtime,scala,scala.collec
 tion,scala.reflect";version="0.0.0"
Import-Package: scala,scala.collection,scala.reflect,scala.runtime
Tool: Bnd-1.44.0

java.io.InvalidClassException: failed to read class descriptor
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1585)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1964)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1888)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1964)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1888)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
at org.jboss.netty.handler.codec.serialization.ObjectDecoderInputStream.readObject(ObjectDecoderInputStream.java:115)
at com.openaf.rmi.common.DefaultObjectEncoder$.decode(RMICommon.scala:33)
at com.openaf.rmi.client.ClientHandler.messageReceived(ClientPipelineFactory.scala:43)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296)
at org.jboss.netty.handler.codec.frame.FrameDecoder.unfoldAndFireMessageReceived(FrameDecoder.java:363)
at org.jboss.netty.handler.codec.frame.FrameDecoder.callDecode(FrameDecoder.java:345)
at org.jboss.netty.handler.codec.frame.FrameDecoder.messageReceived(FrameDecoder.java:211)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255)
at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:94)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.processSelectedKeys(AbstractNioWorker.java:372)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:246)
at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:38)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.ClassNotFoundException: com.openaf.utils.TestObject not found by travel.api [9]
at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1460)
at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:72)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1843)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at org.jboss.netty.handler.codec.serialization.ClassLoaderClassResolver.resolve(ClassLoaderClassResolver.java:30)
at org.jboss.netty.handler.codec.serialization.CachingClassResolver.resolve(CachingClassResolver.java:39)
at org.jboss.netty.handler.codec.serialization.CompactObjectInputStream.readClassDescriptor(CompactObjectInputStream.java:55)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1583)
... 28 more

ありがとう、ニック。

4

3 に答える 3

6

これは実際、デシリアライゼーションの深刻な欠点のように聞こえますか? 適切なデシリアライザーは、負荷の原因となったクラスのクラス ローダーを使用する必要があります。親オブジェクトがまだ存在しないため、特定のクラスローダーは最上位オブジェクトにのみ使用する必要があります。

したがって、この場合、特定のクラス ローダーを使用して PageData をロードします。PageData のローダーを使用して TableData をロードし、TableData のローダーを使用して TestObject をロードする必要があります。これは、VM がクラスをロードするために使用するモデルであるため、使用するデシリアライザーが実際に脳に損傷を受けていない限り、これが失敗する論理的な理由はありません。Java デシリアライザーがこれを行うことに驚きました。VM とは異なるルールを使用するため、この動作は重大なエラーだと思います。

モジュール性は実装クラスを隠すことに関するものであるため、シリアライゼーションは OSGi の問題です。デシリアライゼーションは、これらのプライベート クラスにアクセスする傾向があり、モジュール性のアンチテーゼです。ただし、これには非常に優れた解決策があります (Dynamic-ImportPackage は含まれていません。これは、単純な Java を使用するよりも複雑で高価な方法で JAR 地獄に戻ります)。基本的なトリックは、プライベート/一時的に必要なクラスにアクセスできるパブリック API からルート オブジェクトを取得することです。うーん、これはサービスのように聞こえませんか?

解決

これについて人々がどれほど否定的であるかを見て、Java シリアライゼーション (つまり、ObjectInputStream と ObjectOutputStream) で問題を解決する方法の小さな例を示します。あなたの質問では、私がよく知らないクラスである ObjectDecoderInputStream に言及しています。

セットアップは次のとおりです。

Bundle A:    class a.A { B b; }   (import b)
Bundle B:    class b.B { C c; }   (import c)
Bundle C:    class c.C { }

それでは、まずオブジェクトをシリアライズしましょう:

ByteArrayOutputStream bous = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bous);

oos.writeObject(this);
oos.close();

今、難しい部分です。resolveObject メソッドをオーバーライドします。これにより、実際に適切なクラスのロードを行う機会が得られます ...

ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bous.toByteArray())) {
    Set<ClassLoader>    lhs = new LinkedHashSet<ClassLoader>();
    {
        // Keep a set if discovered class loaders
        lhs.add(getClass().getClassLoader());
    }

    @Override
    protected Class< ? > resolveClass(ObjectStreamClass desc) 
         throws ClassNotFoundException, IOException {

         for (ClassLoader cl : lhs) try {
             Class< ? > c = cl.loadClass(name);

             // we found the class, so we can use its class loader,
             // it is in the proper class space  if the uses constraints 
             // are set properly (and you're using bnd so you should be ok)

             lhs.add(c.getClassLoader());

             // The paranoid among us would check
             // the serial uuid here ...
             // long uuid = desc.getSerialVersionUID();
             // Field field = c.getField("serialVersionUID");
             // assert uuid == field.get(null)

             return c;
         } catch (Exception e) {
           // Ignore
         }

         // Fallback (for void and primitives)
         return super.resolveClass(desc);
     }
 };

 // And now we've successfully read the object ...

 A clone = (A) in.readObject();

これは、一時的なグラフが適切にエクスポートされている場合にのみ機能することに注意してください。つまり、できる場合はnew TableData、これも機能するはずです。たとえば、インターフェイスから実装を取得する場合は、機能しません。インターフェイス クラスは impl に接続されていません。クラス。つまり、TableData を拡張した TableDataImpl があれば、大変なことになります。そのような場合、実装の「ドメイン」を見つけるためのサービスが必要です。

幸運を。

于 2012-12-14T09:42:57.033 に答える
2

はい、これは難しい問題です。多くの場合、問題はさらに深刻で、ストリームを逆シリアル化するためにどのバンドルが必要になるかさえわからない場合があります。これらの場合、コンパイル時の依存関係は実行時の依存関係と同じではありません。

これらの状況に対処するために、DynamicImports-PackageまたはBundleWiring APIを使用しました。どちらもうまくいきましたが、動的インポートの方が簡単です。

このクラスのロードが必要な部分をできるだけ別のバンドルに分離し、そのバンドルで DynamicImport を使用するようにします。

がんばれ、フランク

于 2012-12-13T14:48:43.683 に答える
2

それを行う方法は他にありません。

デシリアライズされたオブジェクト ツリーに含まれるすべての依存関係を、実行しようとしているバンドルに明示的に記述する必要があります。

すべてのドメイン オブジェクトを 1 つのバンドル、たとえばモデルに入れ、他のすべてのバンドルをそれに依存させることができます。

于 2012-12-13T14:44:05.743 に答える