Spring MVC Model Versioning

Spring MVC binding for using Jackson Model Versioning Module.

License

License

GroupId

GroupId

com.github.jonpeterson
ArtifactId

ArtifactId

spring-webmvc-model-versioning
Last Version

Last Version

1.0.0
Release Date

Release Date

Type

Type

jar
Description

Description

Spring MVC Model Versioning
Spring MVC binding for using Jackson Model Versioning Module.
Project URL

Project URL

https://github.com/jonpeterson/spring-webmvc-model-versioning
Source Code Management

Source Code Management

https://github.com/jonpeterson/spring-webmvc-model-versioning

Download spring-webmvc-model-versioning

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.jonpeterson/spring-webmvc-model-versioning/ -->
<dependency>
    <groupId>com.github.jonpeterson</groupId>
    <artifactId>spring-webmvc-model-versioning</artifactId>
    <version>1.0.0</version>
</dependency>
// https://jarcasting.com/artifacts/com.github.jonpeterson/spring-webmvc-model-versioning/
implementation 'com.github.jonpeterson:spring-webmvc-model-versioning:1.0.0'
// https://jarcasting.com/artifacts/com.github.jonpeterson/spring-webmvc-model-versioning/
implementation ("com.github.jonpeterson:spring-webmvc-model-versioning:1.0.0")
'com.github.jonpeterson:spring-webmvc-model-versioning:jar:1.0.0'
<dependency org="com.github.jonpeterson" name="spring-webmvc-model-versioning" rev="1.0.0">
  <artifact name="spring-webmvc-model-versioning" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.github.jonpeterson', module='spring-webmvc-model-versioning', version='1.0.0')
)
libraryDependencies += "com.github.jonpeterson" % "spring-webmvc-model-versioning" % "1.0.0"
[com.github.jonpeterson/spring-webmvc-model-versioning "1.0.0"]

Dependencies

compile (2)

Group / Artifact Type Version
com.github.jonpeterson : jackson-module-model-versioning jar 1.1.1
org.springframework : spring-webmvc jar [4.2.0.RELEASE,)

test (10)

Group / Artifact Type Version
org.codehaus.groovy : groovy-all jar 2.4.5
javax.servlet : javax.servlet-api jar 3.0.1
org.slf4j : jcl-over-slf4j jar 1.7.12
org.slf4j : log4j-over-slf4j jar 1.7.12
org.spockframework : spock-core jar 1.0-groovy-2.4
org.spockframework : spock-spring jar 1.0-groovy-2.4
org.springframework : spring-test jar [4.2.0.RELEASE,)
org.springframework.boot : spring-boot-starter-jetty jar 1.4.0.RELEASE
org.springframework.boot : spring-boot-starter-web jar 1.4.0.RELEASE
org.springframework.boot : spring-boot-starter-test jar 1.4.0.RELEASE

Project Modules

There are no modules declared in this project.

Spring MVC Model Versioning

Spring MVC binding for using Jackson Model Versioning Module.

Example

Note: This example is using Groovy for brevity, but it is not required.

Example data model versions

Car data v1

{
  "model": "honda:civic",
  "year": 2016,
  "new": "true"
}

Car data v2

{
  "make": "honda",
  "model": "civic",
  "year": 2016,
  "new": "true"
}

Car data v3

{
  "make": "honda",
  "model": "civic",
  "year": 2016,
  "used": false
}

Define the model POJO and version converters

Create a POJO for the newest version of the data. Using the Jackson Model Versioning Module, annotate the model with the current version and specify the converter class to use when deserializing from a potentially old version of the model to the current version and the converter class to use when serializing from the current version to a potentially old version of the model. Also add a field (or getter/setter methods) that specify the version that the model to be serialized to.

@JsonVersionedModel(currentVersion = '3',
                    toCurrentConverterClass = ToCurrentCarConverter,
                    toPastConverterClass = ToPastCarConverter)
class Car {
    String make
    String model
    int year
    boolean used

    @JsonSerializeToVersion
    String serializeToVersion
}

Create the "up" converter and provide logic for how old versions should be converted to the current version.

class ToCurrentCarConverter implements VersionedModelConverter {
    @Override
    def ObjectNode convert(ObjectNode modelData, String modelVersion,
                           String targetModelVersion, JsonNodeFactory nodeFactory) {

        // model version is an int
        def version = modelVersion as int

        // version 1 had a single 'model' field that combined 'make' and 'model' with a colon delimiter
        if(version <= 1) {
            def makeAndModel = modelData.get('model').asText().split(':')
            modelData.put('make', makeAndModel[0])
            modelData.put('model', makeAndModel[1])
        }

        // version 1-2 had a 'new' text field instead of a boolean 'used' field
        if(version <= 2)
            modelData.put('used', !Boolean.parseBoolean(modelData.remove('new').asText()))
    }
}

Create the "down" converter and provide logic for how the current version should be converted to an old version.

class ToPastCarConverter implements VersionedModelConverter {

    @Override
    def ObjectNode convert(ObjectNode modelData, String modelVersion,
                           String targetModelVersion, JsonNodeFactory nodeFactory) {

        // model version is an int
        def version = modelVersion as int
        def targetVersion = targetModelVersion as int

        // version 1 had a single 'model' field that combined 'make' and 'model' with a colon delimiter
        if(targetVersion <= 1 && version > 1)
            modelData.put('model', "${modelData.remove('make').asText()}:${modelData.get('model').asText()}")

        // version 1-2 had a 'new' text field instead of a boolean 'used' field
        if(targetVersion <= 2 && version > 2)
            modelData.put('new', !modelData.remove('used').asBoolean() as String)
    }
}

Set up a REST endpoint

Configure the Jackson ObjectMapper used by Spring MVC to use the Jackson Model Versioning Module and import VersionedModelResponseBodyAdvice.

@Configuration
@Import(VersionedModelResponseBodyAdvice)
class SpringMvcVersioningConfiguration {

    @Bean
    Jackson2ObjectMapperBuilder objectMapperBuilder() {
        return Jackson2ObjectMapperBuilder.json().modulesToInstall(new VersioningModule())
    }
}

Create a REST endpoint

@RequestMapping(method = RequestMethod.POST,
                path = '/',
                consumes = MediaType.APPLICATION_JSON_VALUE,
                produces = MediaType.APPLICATION_JSON_VALUE)
@VersionedResponseBody(defaultVersion = '2',
                       headerName = 'Model-Version',
                       queryParamName = 'modelVersion')
Car createCar(@RequestBody Car car) {
    return carRepository.insert(car)
}

Test the endpoint

All that's left is to test it out.

def restTemplate = new RestTemplate(
    messageConverters: [
        new MappingJackson2HttpMessageConverter(new ObjectMapper().registerModule(new VersioningModule()))
    ]
)

// POST version 1 JSON and request version 2 JSON response via URL query param
println restTemplate.postForObject(
    'http://localhost:8080/?modelVersion=2',
    '{"model": "honda:civic", "year": 2016, "new": "true", "modelVersion": "1"],
    String
)
// prints '{"make":"honda","model":"civic","year":2016,"new":"true","modelVersion":"2"}'

// POST version 1 JSON and request version 2 JSON response via HTTP header
println restTemplate.exchange(
    'http://localhost:8080/',
    HttpMethod.POST,
    new HttpEntity<String>(
        '{"model": "honda:civic", "year": 2016, "new": "true", "modelVersion": "1"]',
        new LinkedMultiValueMap<String, String>('Model-Version': '2')
    ),
    String
)
// prints '{"make":"honda","model":"civic","year":2016,"new":"true","modelVersion":"2"}'

More Examples

See the tests under src/test/groovy for more.

Compatibility

  • Requires Java 6 or higher
  • Requires Spring 4.2 or higher (tested with Spring 4.2 - 4.3)
  • Requires Jackson 2.2 or higher (uses a version of Jackson Model Versioning Module which is tested with Jackson 2.2 - 2.8).

Getting Started with Gradle

dependencies {
    compile 'com.github.jonpeterson:spring-webmvc-model-versioning:1.0.0'
}

Getting Started with Maven

<dependency>
    <groupId>com.github.jonpeterson</groupId>
    <artifactId>spring-webmvc-model-versioning</artifactId>
    <version>1.0.0</version>
</dependency>

JavaDoc

Changelog

Versions

Version
1.0.0