@Endpoint с примитивными типами данных - без адаптера для конечной точки
Я пытаюсь сгенерировать CXF-Java-классы из WSDL с gradle (WSDL2Java), чтобы я мог использовать его в своем SpringBoot-приложении.
Приложение предоставляет SOAP-конечную точку. Мой ответ, который я хочу, состоит из простого логического:
Запрос:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header/>
<soap:Body>
<ns:isActiveRequest xmlns:ns=\"http://problem.com\"><id>1234</id></ns:isActiveRequest>
</soap:Body>
</soap:Envelope>
Отклик:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header/>
<soap:Body>
<ns:isActiveResponse xmlns:ns="http://problem.com">true</ns3:isActiveResponse>
</soap:Body>
</soap:Envelope>
Мне не удалось определить этот ответ в моем WSDL, чтобы он работал после генерации CXF с аннотацией @Endpoint в моем приложении SpringBoot. Я всегда получаю сообщение об ошибке: "Нет адаптера для конечной точки... Аннотирована ли ваша конечная точка с помощью @Endpoint или реализован поддерживаемый интерфейс, такой как MessageHandler или PayloadEndpoint?"
Обычно у меня нет проблем с CXF и Endpoint со сложными типами ответов, потому что он генерирует @XmlRootElement. Но он не будет делать это с примитивным типом в ответе и не сможет распознать мой метод в @Endpoint-Annotation.
[...]
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
@Endpoint
public class SoapEndpoint implements Problem {
private static final String TARGET_NAMESPACE = "http://problem.com";
@PayloadRoot(namespace = TARGET_NAMESPACE, localPart = "isActiveRequest")
@ResponsePayload
@Secured(Roles.PERMISSION)
public boolean isActive(@RequestPayload IsActiveRequest request) {
return true;
}
WSDL, который я использовал для генерации, выглядит следующим образом:
<wsdl:types>
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="unqualified" targetNamespace="http://problem.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="isActiveRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="id" maxOccurs="1" minOccurs="1"
nillable="false" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="isActiveResponse" type="xs:boolean"/>
</xs:schema>
</wsdl:types>
<wsdl:message name="isActiveRequest">
<wsdl:part name="parameters" element="tns:isActiveRequest"></wsdl:part>
</wsdl:message>
<wsdl:message name="isActiveResponse">
<wsdl:part name="parameters" element="tns:isActiveResponse"></wsdl:part>
</wsdl:message>
<wsdl:portType name="problem">
<wsdl:operation name="isActive">
<wsdl:input message="tns:isActiveRequest" name="isActiveRequest"/>
<wsdl:output message="tns:isActiveResponse" name="isActiveResponse"/>
<wsdl:fault message="tns:myException" name="fault"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="problem_SOAPBinding" type="tns:problem">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="isActive">
<soap:operation soapAction="isActive"/>
<wsdl:input name="isActiveRequest">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="isActiveResponse">
<soap:body use="literal"/>
</wsdl:output>
<wsdl:fault name="fault">
<soap:fault use="literal" name="fault"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
В моем build.gradle я использую CXF 3.2.1
task wsdl2java(type: JavaExec) {
ext {
outputDir = file(cxfOutputDir)
wsdlFiles = new FileNameByRegexFinder().getFileNames("${wsdlDir}", /.*\.wsdl/)
}
wsdlFiles.each { String wsdlFile ->
outputs.upToDateWhen { false }
outputs.dir outputDir
main = 'org.apache.cxf.tools.wsdlto.WSDLToJava'
classpath = configurations.cxfTool
args '-d', outputDir
args '-wsdlLocation', "/wsdl/" + new File(wsdlFile).name
args '-verbose'
args '-validate'
args wsdlFile
}
}
compile group: 'org.apache.cxf', name: 'cxf-core', version: "3.2.1"
compile group: 'org.apache.cxf', name: 'cxf-rt-frontend-jaxws', version: "3.2.1"
compile "org.apache.cxf:cxf-spring-boot-starter-jaxws:3.2.1"
cxfTool "org.apache.cxf:cxf-tools-wsdlto-frontend-jaxws:3.2.1"
cxfTool "org.apache.cxf:cxf-tools-wsdlto-databinding-jaxb:3.2.1"
cxfTool "org.apache.cxf:cxf-tools-common:3.2.1"
cxfTool "org.apache.cxf:cxf-tools-wsdlto-core:3.2.1"
2 ответа
Я решил проблему с JAXBElement<>
public JAXBElement<Boolean> isActive(@RequestPayload IsActiveRequest request) {
...
ObjectFactory factory = new ObjectFactory();
return factory.createIsActiveResponse(myBoolean);
}
По умолчанию примитивный тип MethodArgumentResolver отсутствует. Как следует из справки, вы можете использовать более 10 различных преобразователей. Я остановился на JAXB:
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "RemoveObject")
@ResponsePayload
public RemoveObjectResponse removeObject(@RequestPayload JAXBElement<Integer> objectID, @RequestPayload JAXBElement<Boolean> useTrashbin, @RequestPayload JAXBElement<Boolean> ignoreReferences) {
...
}
или вы можете написать собственное расширение для примитивных типов, но, скорее всего, оно того не стоит.
https://docs.spring.io/spring-ws/site/reference/html/server.html#d5e1086
Если вы включите журналы трассировки для регистратора org.springframework.xml, вы сможете увидеть записи журнала потенциальных преобразователей аргументов.
Testing if argument resolver ... supports <your parameter>