是否有对象更改跟踪/版本控制Java API?

时间:2012-04-06 13:54:32

标签: java change-tracking

我知道至少有两个字节码增强器在运行时修改“对象模型”,以允许透明地执行事务。其中一个是Versant VOD的一部分,我每天都在工作中使用,另一个是Terracotta的一部分。可能还有很多其他的东西,例如在ORM中,但是Versant会在我的公司处理这个问题。

我的问题是,是否有这样的开源API可以单独使用,与其设计的产品无关?你可以说一个“hackable”API。它应该只跟踪更改,而不是读取访问权限,这会显着减慢代码速度。换句话说,它不应该要求显式读/写锁定。这需要访问所有执行更改的类,而不仅仅是数据模型,或者需要在内存中保留某种形式的“先前版本”来进行比较。

我试图解决的问题是我在(NoSQL)数据库中“序列化”了“大”(32K到256K)对象图。它们是长寿的,必须定期重新序列化才能拥有变化的“历史”。但序列化的成本相当高,而且大多数变化都很小。

我可以每次完全序列化它们并在流上运行二进制差异,但这听起来非常占用CPU。一个更好的解决方案是修改模型上的写操作以协调更改的API,以便在存储初始“图像”之后,只需要存储协议。

我发现了一些关于Apache Commons Beanutils来比较对象的问题,但这对于就地更改没有用;我需要在每个“业务事务”之间完整地复制模型。

重申一下,我正在寻找同一JVM中的“内存中”API,它不涉及任何外部服务器应用程序。涉及本机代码的API如果在Win,Mac和Mac上可用,则可以。 Linux操作系统。 API不必独立地打包;它必须能够从“父项目”中提取它以形成一个独立的API(父项目许可证必须允许这样做)。

我的对象图将涉及许多大型数组,因此需要得到有效支持。

这些更改不仅适用于审核,而是可以重播或撤消。更准确地说,使用反序列化的初始图和变化列表,我应该得到一个相同的结束图。此外,从结束图开始,应该可以通过反向应用更改返回到初始图。它使用完全相同的功能,但需要更改协议以保留旧值以及新值。

API许可证应与商业用途兼容。

[编辑]到目前为止,我没有得到有用的答案,并且它似乎不像我想要的存在。这让我只有一个选择:让它成为现实。当我有一个有效的实现时,我会在这里发布一个链接作为答案,因为这是我项目的下一步,没有它就无法前进。

[编辑]我偶然发现了这个有点相关的问题:Is there a Java library that can "diff" two Objects?

4 个答案:

答案 0 :(得分:8)

Kryo v1有一个序列化程序,它知道序列化的最后一个数据 并且只发出一个delta。阅读时,它知道收到的最后一个数据 并应用delta。增量在字节级别完成。 Here是。{ 串行器。大部分工作由this class完成。这可以用于一些有用的方式,例如类似于Quake 3的网络。

Kryo v2中省略了这一点,因为AFAIK从未投入使用过。 此外,它没有一套广泛的测试。它可以被移植 并可能做你需要的,或作为你需要的基础。

上面也发布在JVM序列化程序mailing list上。

在对象级别执行它会有点棘手。您可以编写类似于FieldSerializer的内容,同时遍历两个对象图。这将是独立代码,而不是Kryo序列化程序。在每个级别,您可以调用equals。写一个字节,以便在阅读时知道它是否等于。如果不等于,请使用Kryo编写对象。对于同一个对象,将多次调用Equals,尤其是对于深层嵌套的对象。

您可能采用的另一种方法是仅对标量和字符串执行上述操作,即仅对Output类编写的值。问题是走两个对象图。要使用Kryo,我认为您必须复制所有序列化程序才能了解其他对象图。

可能你可以使用Kryo和你自己的输出来收集列表中的值而不是写入它们。用它来“序列化”旧的对象图。现在编写自己的输出的另一个版本,它接受此列表并使用它来序列化新的对象图。每次写入值时,首先使用列表中的下一个对象进行检查。如果等于,则写入1.如果不等于,则写入0然后写入值。

通过使用第一个输出两次,一次在旧图上,一次在新图上,可以使空间效率更高。现在您有两个值列表。使用这些来写一个表示相等的位串。这节省了为每个值写入整个字节的空间,但是具有额外列表的开销。最后,写下所有不相等的值。

要完成此想法,您需要能够反序列化数据。您需要一个自己的Input类版本,它从旧对象图中获取值列表。您的输入首先读取bitstring(或每个值的一个字节)。对于相等的值,它从列表中返回值而不是从数据中读取。如果值不相等,则调用super方法从数据中读取。

我不确定这是否比在字节级执行更快。如果我不得不猜测我会说它可能会更快。将所有值存储在列表中将会有很多装箱/拆箱,即使它们没有更改,这种方法仍会分配所有字段。我怀疑性能是否会成为一个问题,所以我可能只是选择更简单的方法。很难说这是什么......复活delta内容或编写自己的输出/输入类。

如果你想回馈Kryo,那当然会很棒。 :)

答案 1 :(得分:2)

查看Content repository API for JavaArtifactory使用它来控制maven依赖项。 Apache Jackrabbit是此JSR(JSR-283 version 2

的参考实现

答案 2 :(得分:1)

我不知道这样的API,但它不是那么复杂:

  

更好的解决方案是修改模型上的写操作以协调更改的API,以便在存储初始“图像”之后,只需要存储协议。

我想说你只需要2个组件:Action和ActionProcessor

您只需要保留已执行操作的列表(协议)。

interface ActionProcessor{
    void perform(Action action);
    void undoToDate(Date date);
} 

iterface Action{
    Date getDate();
    void perform();
    void undo();
}      

答案 3 :(得分:1)

据我所知,GemFire是Gemstone(现在是VmWare)的企业产品,它的功能类似于Gemstone smalltalk OODB,但后来却用于java。詹姆斯福斯特创造了一个关于宝石如何运作的series of videos。我发现它们非常有趣。 Gemstone有一个免费版本用于构建小型(Seaside web)系统。