Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion .github/actions/warmup-jitpack/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,47 @@ inputs:
tag:
description: "Tag to build on JitPack (e.g. 2.21.5-beta38)"
required: true
attempts:
description: "How many times to ask JitPack before giving up on the tag"
required: false
default: '10'
interval:
description: "Seconds to wait between those attempts"
required: false
default: '60'

runs:
using: composite
steps:
- shell: bash
env:
TAG: ${{ inputs.tag }}
ATTEMPTS: ${{ inputs.attempts }}
INTERVAL: ${{ inputs.interval }}
run: |
set -euo pipefail
base="https://jitpack.io/com/github/${GITHUB_REPOSITORY}/${TAG}"
log=$(mktemp)
# JitPack builds on demand. Requesting the log starts the build and streams its output,
# so that the first consumer of a new version does not have to wait for it.
curl -fsSL --max-time 900 "${base}/build.log" | tail -n 50
#
# JitPack answers with 404 while the tag is missing from the ref list it caches from
# GitHub, which is the normal state for a tag that was pushed moments ago, so that answer
# is waited out. The status has to be read from the response instead of relying on
# curl --fail, because it does not tell 404 apart from a timeout or a 5xx.
for attempt in $(seq 1 "$ATTEMPTS"); do
code=$(curl -sSL --max-time 900 -o "$log" -w '%{http_code}' "${base}/build.log") || code=000
case "$code" in
2??) break ;;
404 | 5?? | 000)
if [ "$attempt" -eq "$ATTEMPTS" ]; then
echo "::error::JitPack did not pick up ${TAG} (last status ${code})"; exit 1
fi
echo "::notice::JitPack does not know ${TAG} yet (${code}), retrying in ${INTERVAL}s (${attempt}/${ATTEMPTS})"
sleep "$INTERVAL" ;;
*) echo "::error::Requesting the JitPack build log failed with ${code}"; exit 1 ;;
esac
done
tail -n 50 "$log"
# The log alone does not reliably report the result, so the artifact itself is requested.
curl -fsS --max-time 60 -o /dev/null "${base}/${GITHUB_REPOSITORY#*/}-${TAG}.pom"
2 changes: 1 addition & 1 deletion .github/workflows/lint-and-test-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v7
- name: Set up java
uses: actions/setup-java@v5
uses: actions/setup-java@v6
with:
java-version: '17'
distribution: 'corretto'
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/release-jackson-lines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ on:

permissions:
contents: write # for pushing tags and creating releases
checks: write # for action-junit-report

jobs:
release-jackson-lines:
Expand All @@ -28,7 +29,7 @@ jobs:
jackson-line: [ '2.21', '2.22' ]
name: "Jackson ${{ matrix.jackson-line }}"
runs-on: ubuntu-latest
timeout-minutes: 20
timeout-minutes: 30
steps:
- name: Plan
id: plan
Expand Down Expand Up @@ -63,7 +64,7 @@ jobs:
ref: ${{ steps.plan.outputs.base_tag }}
- name: Set up java
if: steps.plan.outputs.skip == 'false'
uses: actions/setup-java@v5
uses: actions/setup-java@v6
with:
java-version: '17'
distribution: 'corretto'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
# LTS versions, latest version (if exists)
java-version: [ '17', '21', '25', '26' ]
# Minimum version, latest release version, latest pre-release version (if exists)
kotlin: ['2.1.21', '2.2.21', '2.3.21', '2.4.20-RC']
kotlin: ['2.1.21', '2.2.21', '2.3.21', '2.4.21', '2.5.0-Beta1']
env:
KOTLIN_VERSION: ${{ matrix.kotlin }}
name: "Kotlin ${{ matrix.kotlin }} - Java ${{ matrix.java-version }}"
Expand All @@ -50,7 +50,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v7
- name: 'Set up java ${{ matrix.java-version }}'
uses: actions/setup-java@v5
uses: actions/setup-java@v6
with:
java-version: '${{ matrix.java-version }}'
distribution: 'corretto'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/warmup-jitpack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
warmup-jitpack:
name: warmup-jitpack
runs-on: ubuntu-latest
timeout-minutes: 20
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v7
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ val jacksonVersion = libs.versions.jackson.get()
val generatedSrcPath = "${layout.buildDirectory.get()}/generated/kotlin"

group = groupStr
version = "${jacksonVersion}-beta38"
version = "${jacksonVersion}-beta39"

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]
kotlin = "2.1.21" # The lowest supported version. Mainly for CI, it can be rewritten by environment variable.
jackson = "2.21.6"
jackson = "2.21.7"

# test libs
junit = "6.1.3"
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
Expand Down
52 changes: 41 additions & 11 deletions gradlew.bat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package io.github.projectmapk.jackson.module.kogera.ser.serializers

import com.fasterxml.jackson.annotation.JsonValue
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser.NumberType
import com.fasterxml.jackson.databind.BeanDescription
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializationConfig
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper
import com.fasterxml.jackson.databind.module.SimpleSerializers
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer
import com.fasterxml.jackson.databind.ser.std.StdSerializer
import io.github.projectmapk.jackson.module.kogera.ANY_TO_ANY_METHOD_TYPE
import io.github.projectmapk.jackson.module.kogera.GenericValueClassUnboxConverter
Expand All @@ -29,28 +32,37 @@ import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.math.BigInteger

internal object UByteSerializer : StdSerializer<UByte>(UByte::class.java) {
// The unsigned integer serializers extend StdScalarSerializer so that polymorphic type information
// (e.g. default typing on an `Any` property) is written as for other scalar values,
// and report the integer format to schema generators (StdScalarSerializer assumes a string by default).
internal object UByteSerializer : StdScalarSerializer<UByte>(UByte::class.java) {
private fun readResolve(): Any = UByteSerializer

override fun serialize(value: UByte, gen: JsonGenerator, provider: SerializerProvider) = gen
.writeNumber(value.toShort())

override fun acceptJsonFormatVisitor(visitor: JsonFormatVisitorWrapper, typeHint: JavaType?) = visitIntFormat(visitor, typeHint, NumberType.INT)
}

internal object UShortSerializer : StdSerializer<UShort>(UShort::class.java) {
internal object UShortSerializer : StdScalarSerializer<UShort>(UShort::class.java) {
private fun readResolve(): Any = UShortSerializer

override fun serialize(value: UShort, gen: JsonGenerator, provider: SerializerProvider) = gen
.writeNumber(value.toInt())

override fun acceptJsonFormatVisitor(visitor: JsonFormatVisitorWrapper, typeHint: JavaType?) = visitIntFormat(visitor, typeHint, NumberType.INT)
}

internal object UIntSerializer : StdSerializer<UInt>(UInt::class.java) {
internal object UIntSerializer : StdScalarSerializer<UInt>(UInt::class.java) {
private fun readResolve(): Any = UIntSerializer

override fun serialize(value: UInt, gen: JsonGenerator, provider: SerializerProvider) = gen
.writeNumber(value.toLong())

override fun acceptJsonFormatVisitor(visitor: JsonFormatVisitorWrapper, typeHint: JavaType?) = visitIntFormat(visitor, typeHint, NumberType.LONG)
}

internal object ULongSerializer : StdSerializer<ULong>(ULong::class.java) {
internal object ULongSerializer : StdScalarSerializer<ULong>(ULong::class.java) {
private fun readResolve(): Any = ULongSerializer

override fun serialize(value: ULong, gen: JsonGenerator, provider: SerializerProvider) {
Expand All @@ -60,6 +72,8 @@ internal object ULongSerializer : StdSerializer<ULong>(ULong::class.java) {
else -> gen.writeNumber(BigInteger(value.toString()))
}
}

override fun acceptJsonFormatVisitor(visitor: JsonFormatVisitorWrapper, typeHint: JavaType?) = visitIntFormat(visitor, typeHint, NumberType.BIG_INTEGER)
}

// Class must be UnboxableValueClass.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package io.github.projectmapk.jackson.module.kogera.zPorted.test

import com.fasterxml.jackson.core.JsonParser.NumberType
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonAnyFormatVisitor
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor
import io.github.projectmapk.jackson.module.kogera.defaultMapper
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

/**
* The unsigned integer serializers report the integer format to schema generators
* (previously "any", the StdSerializer default).
*/
internal class UnsignedNumbersJsonFormatVisitorTest {
private class RecordingVisitor : JsonFormatVisitorWrapper.Base() {
var format: String? = null
var numberType: NumberType? = null

override fun expectIntegerFormat(type: JavaType): JsonIntegerFormatVisitor {
format = "integer"

return object : JsonIntegerFormatVisitor.Base() {
override fun numberType(type: NumberType) {
numberType = type
}
}
}

override fun expectAnyFormat(type: JavaType): JsonAnyFormatVisitor? {
format = "any"
return super.expectAnyFormat(type)
}

override fun expectStringFormat(type: JavaType): JsonStringFormatVisitor? {
format = "string"
return super.expectStringFormat(type)
}
}

private fun visit(type: Class<*>): RecordingVisitor =
RecordingVisitor().also { defaultMapper.acceptJsonFormatVisitor(type, it) }

private fun assertInteger(type: Class<*>, numberType: NumberType) {
val visitor = visit(type)

assertEquals("integer", visitor.format)
assertEquals(numberType, visitor.numberType)
}

@Test
fun `UByte is an integer`() = assertInteger(UByte::class.java, NumberType.INT)

@Test
fun `UShort is an integer`() = assertInteger(UShort::class.java, NumberType.INT)

@Test
fun `UInt is an integer`() = assertInteger(UInt::class.java, NumberType.LONG)

@Test
fun `ULong is an integer`() = assertInteger(ULong::class.java, NumberType.BIG_INTEGER)
}
Loading
Loading