Giter Club home page Giter Club logo

kryo-tools's Introduction

kryo-tools

These are shared classes enabling and supporting Kryo serialization and deserialization in a couple of projects (OpenTripPlanner and Conveyal R5).

In package com.conveyal.kryo we define some custom serializers for Guava and Trove collections which are used in both projects. This introduces transitive dependencies on Trove, Guava, and Kryo itself.

The package com.conveyal.object_differ contains library classes that check whether two Java object graphs are identical, for use in serialization round-trip tests. This diffing functionality is actually independent of Kryo and should remain so, but we only use it in serialization tests so it's bundled here with Kryo serializers in a single module.

This performs a semantic equality check between two object graphs in Java. This is intended for use in testing that a round trip through serialization and deserialization reproduces an identical transportation network representation, and that the processs of building that transportation network is reproducible. A system that can do a generalized semantic comparison of any tree of objects is quite complex. Here we try to implement only the minimum feature set needed for our use case in serialization tests.

This code used to be embedded in the R5 project but is now a separate Maven / Git project so that it can be reused in multiple projects, starting with OpenTripPlanner/opentripplanner and conveyal/r5. The object differ started out as a copy of the one supplied by @csolem via the Entur OTP branch at https://github.com/entur/OpenTripPlanner/tree/protostuff_poc but has been mostly rewritten at this point.

It would be possible to factor the object differ out into a separate project, allowing it to be used without depending on a specific version of Kryo. However, the object differ depends on Guava and Trove (as it takes special steps to compare types defined by those libraries), and the Kryo serializers are also for Guava and Trove types. Splitting into two projects would require us to keep the Guava and Trove dependencies in sync and across these two tiny libraries and in all client projects, in addition to the usual effort of tagging and signing releases etc. For this reason, we've always made a well-considered decision not to split them into two projects.

Usage

To use kryo-tools in a Maven project include the following dependency:

<dependency>
    <groupId>com.conveyal</groupId>
    <artifactId>kryo-tools</artifactId>
    <version>1.4.0</version>
    <scope>test</scope>
</dependency>

To use it in a Gradle project include the following dependency:

implementation 'com.conveyal:kryo-tools:1.4.0

Or if using it only in tests:

testImplementation 'com.conveyal:kryo-tools:1.4.0

This project was renamed from object-differ when serializers were added, so artifacts with versions 1.0 and 1.1 exist with that name.

TODO: configuration and usage information, testing on complex objects within OTP.

Using object-differ with Java 11+ Modules

The object differ works by reflection, which is by default not allowed under the Java module system. Applications depending on the kryo-tools object differ will not work without some adjustments. Rather than simply opening large amounts of classes to reflection all the time, a more targeted solution is to add exceptions only when running your tests (since object-differ is only really intended for testing and not needed during normal program execution). See this article:

https://sormuras.github.io/blog/2018-09-11-testing-in-the-modular-world.html#white-box-modular-testing-with-extra-java-command-line-options

In Gradle this might look like:

test {
    useJUnitPlatform()
    jvmArgs = ['--add-opens=java.base/java.io=ALL-UNNAMED',
               '--add-opens=java.base/java.time=ALL-UNNAMED',
               '--add-opens=java.base/java.time.zone=ALL-UNNAMED',
               '--add-opens=java.base/java.lang=ALL-UNNAMED']
}

In Maven:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M7</version>
    <configuration>
        <!-- we have to fork the JVM during tests so that the argLine is passed along -->
        <forkCount>3</forkCount>
        <!-- enable the restricted reflection under Java 11 so that the ObjectDiffer works
             the @{argLine} part is there to allow jacoco to insert its arguments as well
        -->
        <argLine>
            @{argLine}
            -Xmx2G
            -Dfile.encoding=UTF-8
            --add-opens java.base/java.io=ALL-UNNAMED
            --add-opens java.base/java.lang=ALL-UNNAMED
            --add-opens java.base/java.math=ALL-UNNAMED
            --add-opens java.base/java.net=ALL-UNNAMED
        </argLine>
    </configuration>
</plugin>

kryo-tools's People

Contributors

abyrd avatar leonardehrenfried avatar

Watchers

David Emory avatar Trevor Gerhardt avatar James Cloos avatar Anson Stewart avatar  avatar

kryo-tools's Issues

Java 11 module compatibility

I added a module-info.java in 8152211, however during the build Maven warns us not to publish any artifact based on automatic filename based module names. This project still has three:

[WARNING] *******************************************************************************************************************************************************************************
[WARNING] * Required filename-based automodules detected: [kryo-4.0.2.jar, trove4j-3.0.3.jar, slf4j-api-1.7.25.jar]. Please don't publish this project to a public artifact repository! *
[WARNING] *******************************************************************************************************************************************************************************

The new Kryo 5 release will declare a module name. New SLF4J versions already have module names. The tricky one is Trove, which no longer has regular releases. Someone did a 3.1 release
recently under the new name net.sf.trove4j:core but I can only get this to build by requiring the module "core" which is strange - are they claiming this module name at the highest level of the namespace?

We need to start at the leaf nodes of the dependency tree and work up to release all modular artifacts. Unfortunately it seems like projects are migrating very
slowly to named modules: https://blog.frankel.ch/hard-look-state-java-modularization/

Update dependency com.esotericsoftware:kryo to 5.1.0

Version 5.1.0 has a dependency to newer version of objenesis, which fixes this:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.objenesis.instantiator.basic.ObjectStreamClassInstantiator (file:/C:/proj/otp/otp-2.1.0-SNAPSHOT-shaded.jar) to method java.io.ObjectStreamClass.newInstance()
WARNING: Please consider reporting this to the maintainers of org.objenesis.instantiator.basic.ObjectStreamClassInstantiator
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

Then OpenTripPlanner (which in turn depends on kryo-tools) can be run with Java 16, because Java 16 does not by default just log a warning, but aborts execution.

https://mvnrepository.com/artifact/com.esotericsoftware/kryo/5.1.0

Object comparison fails on TreeMaps

[Copied over from conveyal/r5#482 because this object-differ project was factored out of r5]

Over on opentripplanner/OpenTripPlanner#2681 @pailakka showed that comparisons of TreeMaps fails.

The problem lies in the code that checks whether missing entries are represented the same way in both maps. This is only particularly important for Trove primitive maps, but I implemented it in a way that checks every kind of map, using an unlikely integer as a key. Because of type erasure in Java and the fact that the collections predate generics, the methods on Maps will generally accept any object of any class as a key even when that Map is keyed on a specific class, i.e. you can call get(Integer) even on a Map<String, String>, it's just considered a missing element. This worked fine for all the Map implementations I tried, but it does not work for TreeMaps because they use a Comparator to establish the ordering of they keys, so all keys must be mutually comparable.

The solution is probably to switch to an alternate implementation in which the Map wrappers include a method to fetch the representation of a missing value from their wrapped maps. Indeed I started doing that at one point but thought it more robust to use this more general technique, which would be impervious to bugs or omissions in the map wrapper classes.

For the time being we need to exclude TreeMaps from comparisons in tests, either by excluding the entire class or excluding TreeMap fields by name. As far as I know base OTP does not use TreeMaps, this is only an issue with third-party forks. This comparison tool does not attempt to cover all possible use cases, but rather be the minimal system that works for our use cases, so this is not a high priority fix. The problem just needs to be documented in case we run into it again.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.