Skip to content

Commit 6911c54

Browse files
committed
Use Jackson 3 annotations and mappers.
1 parent 9b53648 commit 6911c54

File tree

80 files changed

+471
-604
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+471
-604
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ Running S3Mock in unit tests is still supported by using [TestContainers](https:
143143
* Features and fixes
144144
* Get object with range now returns the same headers as non-range calls.
145145
* Refactorings
146+
* Use Jackson 3 annotations and mappers.
146147
* AWS has deprecated SDK for Java v1 and will remove support EOY 2025.
147148
* Remove Java SDK v1.
148149
* JUnit 4.x deprecation

integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/S3TestBase.kt

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ package com.adobe.testing.s3mock.its
1818
import aws.smithy.kotlin.runtime.net.url.Url
1919
import com.ctc.wstx.api.WstxOutputProperties
2020
import com.fasterxml.jackson.annotation.JsonInclude
21-
import com.fasterxml.jackson.dataformat.xml.XmlMapper
22-
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser
23-
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator
24-
import com.fasterxml.jackson.module.kotlin.KotlinModule
2521
import org.apache.http.client.config.RequestConfig
2622
import org.apache.http.impl.client.CloseableHttpClient
2723
import org.apache.http.impl.client.HttpClientBuilder
@@ -63,6 +59,9 @@ import software.amazon.awssdk.services.s3.presigner.S3Presigner
6359
import software.amazon.awssdk.transfer.s3.S3TransferManager
6460
import software.amazon.awssdk.utils.AttributeMap
6561
import tel.schich.awss3postobjectpresigner.S3PostObjectPresigner
62+
import tools.jackson.dataformat.xml.XmlMapper
63+
import tools.jackson.dataformat.xml.XmlReadFeature
64+
import tools.jackson.dataformat.xml.XmlWriteFeature
6665
import java.io.ByteArrayInputStream
6766
import java.io.File
6867
import java.io.IOException
@@ -646,15 +645,15 @@ internal abstract class S3TestBase {
646645
val MAPPER: XmlMapper =
647646
XmlMapper
648647
.builder()
649-
.addModule(KotlinModule.Builder().build())
650648
.findAndAddModules()
651-
.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
652-
.enable(ToXmlGenerator.Feature.AUTO_DETECT_XSI_TYPE)
653-
.enable(FromXmlParser.Feature.AUTO_DETECT_XSI_TYPE)
649+
.enable(XmlWriteFeature.WRITE_XML_DECLARATION)
650+
.enable(XmlWriteFeature.AUTO_DETECT_XSI_TYPE)
651+
.enable(XmlReadFeature.AUTO_DETECT_XSI_TYPE)
652+
.changeDefaultPropertyInclusion { it.withValueInclusion(JsonInclude.Include.NON_EMPTY) }
654653
.build()
655654
.apply {
656-
setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
657-
factory.xmlOutputFactory
655+
tokenStreamFactory()
656+
.xmlOutputFactory
658657
.setProperty(WstxOutputProperties.P_USE_DOUBLE_QUOTES_IN_XML_DECL, true)
659658
}
660659

server/pom.xml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,15 @@
4040
<artifactId>spring-boot-starter-web</artifactId>
4141
</dependency>
4242
<dependency>
43-
<groupId>com.fasterxml.jackson.dataformat</groupId>
43+
<groupId>tools.jackson.dataformat</groupId>
4444
<artifactId>jackson-dataformat-xml</artifactId>
4545
</dependency>
4646
<dependency>
47-
<groupId>com.fasterxml.jackson.datatype</groupId>
48-
<artifactId>jackson-datatype-jsr310</artifactId>
47+
<groupId>tools.jackson.core</groupId>
48+
<artifactId>jackson-databind</artifactId>
4949
</dependency>
5050
<dependency>
51-
<groupId>com.fasterxml.jackson.datatype</groupId>
52-
<artifactId>jackson-datatype-jdk8</artifactId>
53-
</dependency>
54-
<dependency>
55-
<groupId>com.fasterxml.jackson.module</groupId>
51+
<groupId>tools.jackson.module</groupId>
5652
<artifactId>jackson-module-kotlin</artifactId>
5753
</dependency>
5854
<dependency>

server/src/main/kotlin/com/adobe/testing/s3mock/controller/ControllerConfiguration.kt

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ import com.adobe.testing.s3mock.store.KmsKeyStore
2424
import com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_DELETE_MARKER
2525
import com.ctc.wstx.api.WstxOutputProperties
2626
import com.fasterxml.jackson.annotation.JsonInclude
27-
import com.fasterxml.jackson.dataformat.xml.XmlMapper
28-
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser
29-
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator
3027
import jakarta.servlet.Filter
3128
import jakarta.servlet.http.HttpServletRequest
3229
import org.slf4j.Logger
@@ -40,21 +37,24 @@ import org.springframework.http.HttpHeaders
4037
import org.springframework.http.HttpStatus
4138
import org.springframework.http.MediaType
4239
import org.springframework.http.ResponseEntity
43-
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter
40+
import org.springframework.http.converter.xml.JacksonXmlHttpMessageConverter
4441
import org.springframework.web.bind.annotation.ControllerAdvice
4542
import org.springframework.web.bind.annotation.ExceptionHandler
4643
import org.springframework.web.filter.CommonsRequestLoggingFilter
4744
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer
4845
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
4946
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
47+
import tools.jackson.dataformat.xml.XmlMapper
48+
import tools.jackson.dataformat.xml.XmlReadFeature
49+
import tools.jackson.dataformat.xml.XmlWriteFeature
5050

5151
@Configuration
5252
@EnableConfigurationProperties(ControllerProperties::class)
5353
class ControllerConfiguration : WebMvcConfigurer {
5454
@Bean
5555
fun kmsFilter(
5656
kmsKeyStore: KmsKeyStore,
57-
messageConverter: MappingJackson2XmlHttpMessageConverter
57+
messageConverter: JacksonXmlHttpMessageConverter
5858
): Filter = KmsValidationFilter(kmsKeyStore, messageConverter)
5959

6060
override fun configureContentNegotiation(configurer: ContentNegotiationConfigurer) {
@@ -82,28 +82,30 @@ class ControllerConfiguration : WebMvcConfigurer {
8282
/**
8383
* Creates an HttpMessageConverter for XML.
8484
*
85-
* @return The configured [MappingJackson2XmlHttpMessageConverter].
85+
* @return The configured [JacksonXmlHttpMessageConverter].
8686
*/
8787
@Bean
88-
fun messageConverter(): MappingJackson2XmlHttpMessageConverter {
88+
fun messageConverter(): JacksonXmlHttpMessageConverter {
8989
val mediaTypes = listOf(
9090
MediaType.APPLICATION_XML,
9191
MediaType.APPLICATION_FORM_URLENCODED,
9292
MediaType.APPLICATION_OCTET_STREAM
9393
)
9494

95-
return MappingJackson2XmlHttpMessageConverter().apply {
96-
supportedMediaTypes = mediaTypes
97-
(objectMapper as XmlMapper).apply {
98-
setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
99-
enable(ToXmlGenerator.Feature.AUTO_DETECT_XSI_TYPE)
100-
enable(FromXmlParser.Feature.AUTO_DETECT_XSI_TYPE)
101-
enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
102-
factory.xmlOutputFactory.setProperty(
103-
WstxOutputProperties.P_USE_DOUBLE_QUOTES_IN_XML_DECL,
104-
true
105-
)
95+
val mapper = XmlMapper.builder()
96+
.findAndAddModules()
97+
.enable(XmlWriteFeature.WRITE_XML_DECLARATION)
98+
.enable(XmlWriteFeature.AUTO_DETECT_XSI_TYPE)
99+
.enable(XmlReadFeature.AUTO_DETECT_XSI_TYPE)
100+
.changeDefaultPropertyInclusion { it.withValueInclusion(JsonInclude.Include.NON_EMPTY) }
101+
.build().apply {
102+
tokenStreamFactory()
103+
.xmlOutputFactory
104+
.setProperty(WstxOutputProperties.P_USE_DOUBLE_QUOTES_IN_XML_DECL, true)
106105
}
106+
107+
return JacksonXmlHttpMessageConverter(mapper).apply {
108+
supportedMediaTypes = mediaTypes
107109
}
108110
}
109111

@@ -143,23 +145,24 @@ class ControllerConfiguration : WebMvcConfigurer {
143145
fun objectCannedAclHeaderConverter(): ObjectCannedAclHeaderConverter = ObjectCannedAclHeaderConverter()
144146

145147
/**
146-
* Spring only provides an ObjectMapper that can serialize but not deserialize XML.
148+
* Builds an XmlMapper for header converters.
147149
*/
148-
private fun xmlMapper(): XmlMapper =
149-
XmlMapper.builder()
150+
private fun xmlMapper(): XmlMapper {
151+
val mapper = XmlMapper.builder()
150152
.findAndAddModules()
151-
.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
152-
.enable(ToXmlGenerator.Feature.AUTO_DETECT_XSI_TYPE)
153-
.enable(FromXmlParser.Feature.AUTO_DETECT_XSI_TYPE)
154-
.build()
155-
.apply {
156-
setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
157-
factory.xmlOutputFactory.setProperty(
158-
WstxOutputProperties.P_USE_DOUBLE_QUOTES_IN_XML_DECL,
159-
true
160-
)
153+
.enable(XmlWriteFeature.WRITE_XML_DECLARATION)
154+
.enable(XmlWriteFeature.AUTO_DETECT_XSI_TYPE)
155+
.enable(XmlReadFeature.AUTO_DETECT_XSI_TYPE)
156+
.changeDefaultPropertyInclusion { it.withValueInclusion(JsonInclude.Include.NON_EMPTY) }
157+
.build().apply {
158+
tokenStreamFactory()
159+
.xmlOutputFactory
160+
.setProperty(WstxOutputProperties.P_USE_DOUBLE_QUOTES_IN_XML_DECL, true)
161161
}
162162

163+
return mapper
164+
}
165+
163166
@Bean
164167
fun taggingHeaderConverter(): TaggingHeaderConverter = TaggingHeaderConverter(xmlMapper())
165168

server/src/main/kotlin/com/adobe/testing/s3mock/controller/KmsValidationFilter.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import org.slf4j.LoggerFactory
2828
import org.springframework.http.HttpHeaders
2929
import org.springframework.http.HttpStatus
3030
import org.springframework.http.MediaType
31-
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter
31+
import org.springframework.http.converter.xml.JacksonXmlHttpMessageConverter
32+
import org.springframework.http.server.ServletServerHttpResponse
3233
import org.springframework.web.filter.OncePerRequestFilter
3334
import java.io.IOException
3435

@@ -43,7 +44,7 @@ internal class KmsValidationFilter
4344
* @param keystore Keystore for validation of KMS Keys
4445
*/(
4546
private val keystore: KmsKeyStore,
46-
private val messageConverter: MappingJackson2XmlHttpMessageConverter
47+
private val messageConverter: JacksonXmlHttpMessageConverter
4748
) : OncePerRequestFilter() {
4849
@Throws(ServletException::class, IOException::class)
4950
override fun doFilterInternal(
@@ -73,7 +74,7 @@ internal class KmsValidationFilter
7374
null,
7475
null
7576
)
76-
messageConverter.objectMapper.writeValue(response.outputStream, error)
77+
messageConverter.write(error, MediaType.APPLICATION_XML, ServletServerHttpResponse(response))
7778
response.flushBuffer()
7879
return
7980
}

server/src/main/kotlin/com/adobe/testing/s3mock/controller/TaggingHeaderConverter.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ package com.adobe.testing.s3mock.controller
1717

1818
import com.adobe.testing.s3mock.dto.Tag
1919
import com.adobe.testing.s3mock.dto.Tagging
20-
import com.fasterxml.jackson.core.JsonProcessingException
21-
import com.fasterxml.jackson.dataformat.xml.XmlMapper
2220
import org.springframework.core.convert.converter.Converter
21+
import tools.jackson.core.JacksonException
22+
import tools.jackson.dataformat.xml.XmlMapper
2323

2424
/**
2525
* Converts values of the [com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_TAGGING] which is sent by the Amazon client.
@@ -46,7 +46,7 @@ class TaggingHeaderConverter(private val xmlMapper: XmlMapper) : Converter<Strin
4646
try {
4747
val tagging = xmlMapper.readValue(source, Tagging::class.java)
4848
tagging.tagSet.tags.takeIf { it.isNotEmpty() }
49-
} catch (e: JsonProcessingException) {
49+
} catch (e: JacksonException) {
5050
throw IllegalArgumentException("Failed to parse XML tags from header: $source", e)
5151
}
5252

server/src/main/kotlin/com/adobe/testing/s3mock/dto/AbortIncompleteMultipartUpload.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ import com.fasterxml.jackson.annotation.JsonProperty
2323
*/
2424
@S3Verified(year = 2025)
2525
data class AbortIncompleteMultipartUpload(
26-
@param:JsonProperty("DaysAfterInitiation")
26+
@param:JsonProperty("DaysAfterInitiation", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
2727
val daysAfterInitiation: Int?
2828
)

server/src/main/kotlin/com/adobe/testing/s3mock/dto/AccessControlPolicy.kt

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,26 @@ package com.adobe.testing.s3mock.dto
1717

1818
import com.adobe.testing.S3Verified
1919
import com.fasterxml.jackson.annotation.JsonCreator
20-
import com.fasterxml.jackson.annotation.JsonIgnore
2120
import com.fasterxml.jackson.annotation.JsonProperty
2221
import com.fasterxml.jackson.annotation.JsonRootName
23-
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper
24-
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty
25-
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement
22+
import tools.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper
23+
import tools.jackson.dataformat.xml.annotation.JacksonXmlProperty
2624

2725
/**
2826
* [API Reference](https://docs.aws.amazon.com/AmazonS3/latest/API/API_AccessControlPolicy.html).
2927
* Use bean-style binding (no-args + fields) to avoid creator conflicts with @JacksonXmlElementWrapper.
3028
*/
3129
@S3Verified(year = 2025)
32-
@JsonRootName("AccessControlPolicy")
33-
@JacksonXmlRootElement(localName = "AccessControlPolicy")
30+
@JsonRootName("AccessControlPolicy", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
3431
class AccessControlPolicy() {
35-
@field:JacksonXmlElementWrapper(localName = "AccessControlList")
36-
@field:JacksonXmlProperty(localName = "Grant")
37-
@field:JsonProperty("Grant")
32+
@field:JacksonXmlElementWrapper(localName = "AccessControlList", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
33+
@field:JacksonXmlProperty(localName = "Grant", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
34+
@field:JsonProperty("Grant", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
3835
var accessControlList: List<Grant>? = null
3936

40-
@field:JsonProperty("Owner")
37+
@field:JsonProperty("Owner", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
4138
var owner: Owner? = null
4239

43-
@get:JsonIgnore
44-
@field:JacksonXmlProperty(isAttribute = true, localName = "xmlns")
45-
var xmlns: String = "http://s3.amazonaws.com/doc/2006-03-01/"
46-
4740
// Convenience constructor for tests; disabled for Jackson
4841
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
4942
constructor(accessControlList: List<Grant>?, owner: Owner?) : this() {
@@ -59,15 +52,13 @@ class AccessControlPolicy() {
5952

6053
if (accessControlList != other.accessControlList) return false
6154
if (owner != other.owner) return false
62-
if (xmlns != other.xmlns) return false
6355

6456
return true
6557
}
6658

6759
override fun hashCode(): Int {
6860
var result = accessControlList?.hashCode() ?: 0
6961
result = 31 * result + (owner?.hashCode() ?: 0)
70-
result = 31 * result + xmlns.hashCode()
7162
return result
7263
}
7364
}

server/src/main/kotlin/com/adobe/testing/s3mock/dto/AmazonCustomerByEmail.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo
2525
@S3Verified(year = 2025)
2626
@JsonTypeInfo(use = JsonTypeInfo.Id.SIMPLE_NAME, property = "xsi:type")
2727
data class AmazonCustomerByEmail(
28-
@param:JsonProperty("EmailAddress")
28+
@param:JsonProperty("EmailAddress", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
2929
val emailAddress: String?
3030
) : Grantee

server/src/main/kotlin/com/adobe/testing/s3mock/dto/Bucket.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ import java.nio.file.Path
2626
*/
2727
@S3Verified(year = 2025)
2828
data class Bucket(
29-
@param:JsonProperty("BucketRegion")
29+
@param:JsonProperty("BucketRegion", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
3030
val bucketRegion: String?,
31-
@param:JsonProperty("CreationDate")
31+
@param:JsonProperty("CreationDate", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
3232
val creationDate: String?,
33-
@param:JsonProperty("Name")
33+
@param:JsonProperty("Name", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
3434
val name: String,
3535
@JsonIgnore
3636
val path: Path?

0 commit comments

Comments
 (0)