9

在我的 osgi 应用程序中,我有三个包travel.apitable.apiutils. travel.api取决于table.api取决于utils。请注意,travel.api它不直接依赖于utils. 我使用 aQute Bnd 来生成清单,我相信它工作正常。清单如下所示。

有一个类叫做PageData,它有一个类型的字段TableData,而它又有一个类型的字段TestObjectPageData位于travel.api,TableData位于table.api,TestObject位于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 进行“深度”生成?

任何帮助将不胜感激!

我使用 felix v4 作为我的 osgi 库。

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的loader是用来加载TableData的,TableData的loader是用来加载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

没有其他方法可以做到这一点。

您必须明确说明反序列化对象树包含在您尝试执行此操作的捆绑包中的所有依赖项。

您可以尝试将所有域对象放入一个包中,比如说模型,然后让所有其他包依赖它。

于 2012-12-13T14:44:05.743 回答
2

是的,这是一个棘手的问题。在许多情况下,问题甚至更糟,甚至可能不知道反序列化流需要哪些包。对于这些,编译时依赖与运行时依赖不同。

为了解决这些情况,我使用了DynamicImports-Package或使用BundleWiring API。两者都很好,但动态导入更容易。

我会说将需要此类加载的部分尽可能多地隔离在一个单独的包中,并让该包使用 DynamicImport。

祝你好运,弗兰克

于 2012-12-13T14:48:43.683 回答