Skip to content

Commit

Permalink
fix #36
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikhael Sokolov committed Apr 17, 2022
1 parent e28ab6b commit ea5b0af
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 31 deletions.
11 changes: 4 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@
<artifactId>kotlin-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.sokomishalov.commons</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-random-core</artifactId>
Expand Down Expand Up @@ -119,7 +113,7 @@
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
Expand Down Expand Up @@ -148,6 +142,9 @@
<wsdlOption>
<wsdl>${project.basedir}/src/test/resources/wsdl/PersonService.wsdl</wsdl>
</wsdlOption>
<wsdlOption>
<wsdl>${project.basedir}/src/test/resources/wsdl/issue-36.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("unused")

package ru.sokomishalov.jackson.dataformat.soap

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package ru.sokomishalov.jackson.dataformat.soap
/**
* @author sokomishalov
*/
data class SoapFault @JvmOverloads constructor(
override val message: String? = null,
val code: String? = null
data class SoapFault internal constructor(
override val message: String?,
val code: String?
) : RuntimeException(message ?: "Unknown SOAP error occurred")
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector
import ru.sokomishalov.jackson.dataformat.soap.ser.SoapEnvelopeSerializers
import ru.sokomishalov.jackson.dataformat.soap.util.NamespaceCache
import java.lang.reflect.Field
import java.lang.reflect.Method
import javax.xml.bind.annotation.XmlAttribute
import javax.xml.bind.annotation.XmlElement
import javax.xml.bind.annotation.XmlElementWrapper
Expand All @@ -44,7 +45,7 @@ open class SoapJaxbAnnotationIntrospector : JaxbAnnotationIntrospector(TypeFacto
ann.annotated.isAnnotationPresent(XmlAttribute::class.java) -> ""

// fields with primitives or collections have parent namespaces
ann.annotated is Field || ClassUtil.isJDKClass(t) -> (ann.annotated as Field).declaringClass.namespace
ann.annotated is Field || ann.annotated is Method || ClassUtil.isJDKClass(t) -> ann.parentNamespace

// other classes have their own namespaces
else -> t.namespace
Expand All @@ -61,5 +62,10 @@ open class SoapJaxbAnnotationIntrospector : JaxbAnnotationIntrospector(TypeFacto
else -> super.findWrapperName(ann)
}

protected val Annotated.parentNamespace: String get() = when (val annotatedElement = annotated) {
is Field -> annotatedElement.declaringClass.namespace
is Method -> annotatedElement.declaringClass.namespace
else -> ""
}
protected val Class<*>.namespace: String get() = NamespaceCache.getNamespace(this)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2021-present Mikhael Sokolov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ru.sokomishalov.jackson.dataformat.soap

/**
* @author sokomishalov
*/
data class SoapMultipleHeaders<H : Any>(val headers: Iterable<H>) : Iterable<H> by headers {
constructor(vararg headers: H) : this(headers.toList())
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.ser.Serializers
import com.fasterxml.jackson.databind.ser.std.StdSerializer
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator
import ru.sokomishalov.jackson.dataformat.soap.util.NamespaceCache
import ru.sokomishalov.jackson.dataformat.soap.SoapAddressingHeaders
import ru.sokomishalov.jackson.dataformat.soap.SoapConstants.SOAP_ADDRESSING_NAMESPACE
import ru.sokomishalov.jackson.dataformat.soap.SoapConstants.XML_SCHEMA_NAMESPACE
import ru.sokomishalov.jackson.dataformat.soap.SoapEnvelope
import ru.sokomishalov.jackson.dataformat.soap.util.NamespaceCache
import javax.xml.bind.JAXBElement
import javax.xml.bind.annotation.XmlRootElement
import javax.xml.namespace.QName
Expand All @@ -38,7 +38,7 @@ internal class SoapEnvelopeSerializers(private val ns: String) : Serializers.Bas
config: SerializationConfig,
type: JavaType,
beanDesc: BeanDescription
): JsonSerializer<*>? = when {
): JsonSerializer<*>? = when {
SoapEnvelope::class.java.isAssignableFrom(type.rawClass) -> SoapEnvelopeSerializer(ns)
JAXBElement::class.java.isAssignableFrom(type.rawClass) -> JaxbElementSerializer
else -> super.findSerializer(config, type, beanDesc)
Expand All @@ -52,21 +52,21 @@ internal class SoapEnvelopeSerializers(private val ns: String) : Serializers.Bas
setNextName(QName(ns, "Envelope"))
writeStartObject()
staxWriter.writeNamespace("xsi", XML_SCHEMA_NAMESPACE)
serializeElement("Header", envelope.header)
serializeElement("Body", envelope.body)
serializeElement(localPart = "Header", element = envelope.header, isHeader = true)
serializeElement(localPart = "Body", element = envelope.body, isHeader = false)
writeEndObject()
}
}
}

private fun ToXmlGenerator.serializeElement(localPart: String, element: Any?) {
private fun ToXmlGenerator.serializeElement(localPart: String, element: Any?, isHeader: Boolean) {
with(this) {
setNextName(QName(ns, localPart))
writeFieldName(localPart)

val annotation = element?.javaClass?.getAnnotation(XmlRootElement::class.java)
when {
element is SoapAddressingHeaders -> {
isHeader && element is SoapAddressingHeaders -> {
writeStartObject()
staxWriter.writeNamespace("wsa", SOAP_ADDRESSING_NAMESPACE)

Expand All @@ -80,6 +80,17 @@ internal class SoapEnvelopeSerializers(private val ns: String) : Serializers.Bas

writeEndObject()
}
isHeader && element is Iterable<*> -> {
writeStartObject()
element.forEach {
if (it != null) {
setNextName(QName(NamespaceCache.getNamespace(it.javaClass), annotation?.name ?: it.javaClass.simpleName))
writeFieldName(annotation?.name ?: it.javaClass.simpleName)
writeObject(it)
}
}
writeEndObject()
}
annotation != null -> {
writeStartObject()
setNextName(QName(NamespaceCache.getNamespace(element.javaClass), annotation.name))
Expand Down Expand Up @@ -116,7 +127,6 @@ internal class SoapEnvelopeSerializers(private val ns: String) : Serializers.Bas
}

internal object JaxbElementSerializer : StdSerializer<JAXBElement<*>>(JAXBElement::class.java) {

override fun serialize(value: JAXBElement<*>?, gen: JsonGenerator, provider: SerializerProvider) {
when {
value != null && !value.isNil -> gen.writeObject(value.value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
package ru.sokomishalov.jackson.dataformat.soap

import com.oracle.xmlns.apps.mdm.customer.GetPersonOutput
import de.addmore.grouplist.grouplist_001.AppMonDetailsStrict
import de.addmore.grouplist.grouplist_001.ControllObject
import de.addmore.grouplist.grouplist_001.GetGroupListRequest
import org.jeasy.random.EasyRandom
import org.jeasy.random.EasyRandomParameters
import org.junit.jupiter.api.Test
import ru.sokomishalov.commons.core.log.Loggable
import ru.sokomishalov.commons.core.string.isNotNullOrBlank
import javax.xml.bind.JAXBContext
import javax.xml.bind.JAXBElement
import javax.xml.namespace.QName
Expand All @@ -41,11 +42,9 @@ class SoapMapperTest {

val envelope = SoapEnvelope(body = body, header = header)

val result = mapper.writeValueAsString(envelope)
val result = mapper.writeValueAsString(envelope).also { println(it) }

logInfo { result }

assertTrue { result.isNotNullOrBlank() }
assertFalse { result.isNullOrBlank() }

listOf(
":Action>",
Expand Down Expand Up @@ -87,7 +86,7 @@ class SoapMapperTest {
val content = readResource("/example/get_person_output_ws_addr.xml")
val soapEnvelope = mapper.readValue<SoapAddressingHeaders?, GetPersonOutput?>(content)

logInfo { mapper.writeValueAsString(soapEnvelope) }
mapper.writeValueAsString(soapEnvelope).also { println(it) }
assertNotNull(soapEnvelope)

assertNotNull(soapEnvelope.header)
Expand All @@ -104,9 +103,9 @@ class SoapMapperTest {
assertNotNull(pojo.listOfSwiPersonIO)
assertEquals(2, pojo.listOfSwiPersonIO?.contact?.size)
pojo.listOfSwiPersonIO.contact.forEach {
assertTrue { it.id.value.isNotNullOrBlank() }
assertTrue { it.firstName.isNotNullOrBlank() }
assertTrue { it.lastName.isNotNullOrBlank() }
assertFalse { it.id.value.isNullOrBlank() }
assertFalse { it.firstName.isNullOrBlank() }
assertFalse { it.lastName.isNullOrBlank() }
}
}

Expand All @@ -115,7 +114,7 @@ class SoapMapperTest {
val content = readResource("/example/soap_fault_1_1.xml")
val soapFault = assertFailsWith<SoapFault> { mapper.readValueBody<GetPersonOutput>(content) }

logInfo { mapper.writeValueAsString(soapFault) }
mapper.writeValueAsString(soapFault).also { println(it) }
assertNotNull(soapFault)
assertNotNull(soapFault)
assertFalse { soapFault.message.isNullOrBlank() }
Expand All @@ -127,17 +126,45 @@ class SoapMapperTest {
val content = readResource("/example/soap_fault_1_2.xml")
val soapFault = assertFailsWith<SoapFault> { mapper.readValueBody<GetPersonOutput>(content) }

logInfo { mapper.writeValueAsString(soapFault) }
mapper.writeValueAsString(soapFault).also { println(it) }
assertNotNull(soapFault)
assertNotNull(soapFault)
assertFalse { soapFault.message.isNullOrBlank() }
assertFalse { soapFault.code.isNullOrBlank() }
}

@Test
fun `GH-36`() {
val first = AppMonDetailsStrict().apply {
bpId = random()
bpName = random()
}
val second = ControllObject().apply {
timeout = random()
}

val header = SoapMultipleHeaders(first, second)
val body = random<GetGroupListRequest>()

val envelope = SoapEnvelope(body = body, header = header)

val result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(envelope).also { println(it) }

listOf(
":AppMonDetailsStrict",
":ControllObject",
":bpId",
":bpName",
":timeout",
).forEach {
assertTrue("$it is not present") { it in result }
}
}

private fun readResource(path: String) = javaClass.getResource(path)?.readText().orEmpty()
private inline fun <reified T> random(): T = RANDOM.nextObject(T::class.java)

companion object : Loggable {
companion object {
private val RANDOM: EasyRandom = EasyRandom(
EasyRandomParameters()
.charset(Charsets.UTF_8)
Expand Down
104 changes: 104 additions & 0 deletions src/test/resources/wsdl/issue-36.wsdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.addmore.de/GroupList/GroupList-001.wsdl"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
targetNamespace="http://www.addmore.de/GroupList/GroupList-001.wsdl">
<wsdl:types>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.addmore.de/GroupList/GroupList-001.wsdl">
<xs:element name="GetGroupListRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="rowCount" type="integer"/>
<xs:element name="banList">
<xs:complexType>
<xs:sequence>
<xs:element name="ban" type="integer" maxOccurs="10"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="GetGroupListResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="rowCount" type="integer"/>
<xs:element name="vpnGroup" type="string" minOccurs="0" maxOccurs="600"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="appMonDetailsStrict">
<xs:complexType>
<xs:sequence>
<element name="bpId" type="string"/>
<element name="bpName" type="string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="controllObject">
<xs:complexType>
<xs:sequence>
<element name="timeout" type="unsignedInt"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="functionalException"></xs:element>
<xs:element name="technicalException"></xs:element>
</schema>
</wsdl:types>
<wsdl:message name="appMonDetailsStrictHeader">
<wsdl:part name="AppMonDetailsStrict" element="tns:appMonDetailsStrict"/>
</wsdl:message>
<wsdl:message name="controllObject">
<wsdl:part name="ControllObject" element="tns:controllObject"/>
</wsdl:message>
<wsdl:message name="functionalException">
<wsdl:part name="FunctionalException" element="tns:functionalException"/>
</wsdl:message>
<wsdl:message name="technicalException">
<wsdl:part name="TechnicalException" element="tns:technicalException"/>
</wsdl:message>
<wsdl:message name="GetGroupListRequest">
<wsdl:part element="tns:GetGroupListRequest" name="GetGroupListRequest"/>
</wsdl:message>
<wsdl:message name="GetGroupListResponse">
<wsdl:part element="tns:GetGroupListResponse" name="GetGroupListResponse"/>
</wsdl:message>
<wsdl:portType name="GroupListPortType">
<wsdl:operation name="GetGroupList">
<wsdl:input message="tns:GetGroupListRequest" name="GetGroupListRequest"/>
<wsdl:output message="tns:GetGroupListResponse" name="GetGroupListResponse"/>
<wsdl:fault message="tns:functionalException" name="functionalException"/>
<wsdl:fault message="tns:technicalException" name="technicalException"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="GroupListEndpointBinding" type="tns:GroupListPortType">
<soap12:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="GetGroupList">
<soap12:operation soapAction="/GroupList-001/GetGroupList" soapActionRequired="true"/>
<wsdl:input>
<soap12:body parts="GetGroupListRequest" use="literal"/>
<soap12:header message="tns:appMonDetailsStrictHeader" part="AppMonDetailsStrict" use="literal"
wsdl:required="true"/>
<soap12:header message="tns:controllObject" part="ControllObject" use="literal"/>
</wsdl:input>
<wsdl:output>
<soap12:body parts="GetGroupListResponse" use="literal"/>
</wsdl:output>
<wsdl:fault name="functionalException">
<soap12:fault name="functionalException" use="literal"/>
</wsdl:fault>
<wsdl:fault name="technicalException">
<soap12:fault name="technicalException" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="GroupList">
<wsdl:port binding="tns:GroupListEndpointBinding" name="GroupListEndpoint">
<soap12:address location="http://www.addmore.de/"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

0 comments on commit ea5b0af

Please sign in to comment.