Invalid wsdl generated by spring-ws when request element does not end with 'Request'

I have to prepare a web service to accept anan already defined wsdl structure. I followed the tutorial found here , with the source code for the test project being downloaded here .

For xsd:

<xs:element name="getCountryRequest"> <xs:complexType> <xs:sequence> <xs:element name="name" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> 

The Wsdl operation for the request returned by the application in order is as follows:

 <wsdl:binding name="CountriesPortSoap11" type="tns:CountriesPort"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="getCountry"> <soap:operation soapAction=""/> <wsdl:input name="getCountryRequest"> <soap:body use="literal"/> </wsdl:input> <wsdl:output name="getCountryResponse"> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> 

But when I change xsd to (no 'Request' in the element name):

 <xs:element name="getCountry"> <xs:complexType> <xs:sequence> <xs:element name="name" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> 

wsdl is invalid and does not matter <input> :

 <wsdl:binding name="CountriesPortSoap11" type="tns:CountriesPort"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="getCountry"> <soap:operation soapAction=""/> <wsdl:output name="getCountryResponse"> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> 

How can i fix this? How can I make the Request -less element correctly displayed, like entering soap operation in wsdl?

+8
source share
4 answers

According to

You can, for example, set Req instead of Request , then the assembly will automatically generate a new GetCountryReq class, then the ApplicationTests and CountryEndpoint code must then be manually adapted, eliminating compilation errors (since they will still point to a pre-existing GetCountryRequest class), but they must also change localPart = "getCountryReq" attribute localPart = "getCountryReq" @PayloadRoot annotation in the CountryEndpoint class.

I tried and the build went fine and the WSDL was updated accordingly.

As for changing the default suffix for another suffix. But what about changing it to an empty suffix?

 wsdl11Definition.setRequestSuffix(""); 

Exception: the suffix must not be empty. Spring does not support it. In accordance with this :

Basically the problem is this:
We must distinguish which elements of the schema are wsdl messages and which are not.
Of all the wsdl messages, we need to find out which input (request) messages.
From all wsdl messages, we must find out which of them are displayed (answers).

The value DefaultWsdl11Definition indicates this with suffixes. Or, more specifically, it delegates the suffixBasedMessagesProvider and SuffixBasedPortTypesProvider for this.
Therefore, if you have another preferred way of determining what an I / O message is doing, you will have to write your own provider and / or porttypesprovider messages.

Simply put: for Spring -WS, there is no general way to determine what constitutes a request and response, rather than using suffixes.

Note. The poster for this post was Arjen Putzma, author of the DefaultWsdl11Definition class (according to javadocs), a component that handles automatic matching based on these conventions.

But it leaves the door open: writing your own SuffixBasedMessagesProvider and SuffixBasedPortTypesProvider . However, it also left everything as private in DefaultWsdl11Definition (where these providers are instantiated), so you will also need to write (copy) your own WSDL11 definition determinant.

Here is the process that I followed then:

Create your own CustomSuffixBasedMessagesProvider by overriding the setRequestSuffix method and deleting the check for an empty suffix, in the isMessageElement method you will need to process the new mapping Create your own setRequestSuffix and removing the setRequestSuffix method and removing the test is set to an empty suffix your own CustomWsdl11Definition as a copy of the existing DefaultWsdl11Definition and create your own creators created above. Modify the WebServiceConfig class method, DefaultWsdl11Definition , to use your own CustomWsdl11Definition to apply the entire setting.

However, an empty suffix comes with a bit of trouble, as that would be good for any element (i.e., each element has an empty suffix). Therefore, I mentioned the processing of isMessageElement , isInputMessage and getOperationName : when growing WSDL, you can easily complete the encoding of the display (for GetCountry, GetCountryResponse - the answer) or the transfer of properties / maps (as suggested in the section mentioned above), but again repeating most of your XSD is back in your WebServiceConfig configuration class, making it difficult to maintain, troubleshoot, share.

So, I really suggest not to make this trip and stick to the default suffix (if possible) or create a new one, but avoid an empty suffix (they are still not allowed by the library).

But since I went on a trip, here is a working solution:

Custom class MySuffixBasedMessagesProvider (import removed for brevity):

 package hello; public class MySuffixBasedMessagesProvider extends SuffixBasedMessagesProvider { protected String requestSuffix = DEFAULT_REQUEST_SUFFIX; public String getRequestSuffix() { return this.requestSuffix; } public void setRequestSuffix(String requestSuffix) { this.requestSuffix = requestSuffix; } @Override protected boolean isMessageElement(Element element) { if (isMessageElement0(element)) { String elementName = getElementName(element); Assert.hasText(elementName, "Element has no name"); return elementName.endsWith(getResponseSuffix()) || (getRequestSuffix().isEmpty() ? true : elementName.endsWith(getRequestSuffix())) || elementName.endsWith(getFaultSuffix()); } return false; } protected boolean isMessageElement0(Element element) { return "element".equals(element.getLocalName()) && "http://www.w3.org/2001/XMLSchema".equals(element.getNamespaceURI()); } } 

Custom class MySuffixBasedPortTypesProvider (import removed for brevity):

 package hello; public class MySuffixBasedPortTypesProvider extends SuffixBasedPortTypesProvider { private String requestSuffix = DEFAULT_REQUEST_SUFFIX; public String getRequestSuffix() { return requestSuffix; } public void setRequestSuffix(String requestSuffix) { this.requestSuffix = requestSuffix; } @Override protected String getOperationName(Message message) { String messageName = getMessageName(message); String result = null; if (messageName != null) { if (messageName.endsWith(getResponseSuffix())) { result = messageName.substring(0, messageName.length() - getResponseSuffix().length()); } else if (messageName.endsWith(getFaultSuffix())) { result = messageName.substring(0, messageName.length() - getFaultSuffix().length()); } else if (messageName.endsWith(getRequestSuffix())) { result = messageName.substring(0, messageName.length() - getRequestSuffix().length()); } } return result; } @Override protected boolean isInputMessage(Message message) { String messageName = getMessageName(message); return messageName != null && !messageName.endsWith(getResponseSuffix()); } private String getMessageName(Message message) { return message.getQName().getLocalPart(); } } 

Custom class MyWsdl11Definition (essentially the default copy, just creating the classes above, the import is removed for brevity):

 package hello; public class MyWsdl11Definition implements Wsdl11Definition, InitializingBean { private final InliningXsdSchemaTypesProvider typesProvider = new InliningXsdSchemaTypesProvider(); private final SuffixBasedMessagesProvider messagesProvider = new MySuffixBasedMessagesProvider(); private final SuffixBasedPortTypesProvider portTypesProvider = new MySuffixBasedPortTypesProvider(); private final SoapProvider soapProvider = new SoapProvider(); private final ProviderBasedWsdl4jDefinition delegate = new ProviderBasedWsdl4jDefinition(); private String serviceName; public MyWsdl11Definition() { delegate.setTypesProvider(typesProvider); delegate.setMessagesProvider(messagesProvider); delegate.setPortTypesProvider(portTypesProvider); delegate.setBindingsProvider(soapProvider); delegate.setServicesProvider(soapProvider); } public void setTargetNamespace(String targetNamespace) { delegate.setTargetNamespace(targetNamespace); } public void setSchema(XsdSchema schema) { typesProvider.setSchema(schema); } public void setSchemaCollection(XsdSchemaCollection schemaCollection) { typesProvider.setSchemaCollection(schemaCollection); } public void setPortTypeName(String portTypeName) { portTypesProvider.setPortTypeName(portTypeName); } public void setRequestSuffix(String requestSuffix) { portTypesProvider.setRequestSuffix(requestSuffix); messagesProvider.setRequestSuffix(requestSuffix); } public void setResponseSuffix(String responseSuffix) { portTypesProvider.setResponseSuffix(responseSuffix); messagesProvider.setResponseSuffix(responseSuffix); } public void setFaultSuffix(String faultSuffix) { portTypesProvider.setFaultSuffix(faultSuffix); messagesProvider.setFaultSuffix(faultSuffix); } public void setCreateSoap11Binding(boolean createSoap11Binding) { soapProvider.setCreateSoap11Binding(createSoap11Binding); } public void setCreateSoap12Binding(boolean createSoap12Binding) { soapProvider.setCreateSoap12Binding(createSoap12Binding); } public void setSoapActions(Properties soapActions) { soapProvider.setSoapActions(soapActions); } public void setTransportUri(String transportUri) { soapProvider.setTransportUri(transportUri); } public void setLocationUri(String locationUri) { soapProvider.setLocationUri(locationUri); } public void setServiceName(String serviceName) { soapProvider.setServiceName(serviceName); this.serviceName = serviceName; } @Override public void afterPropertiesSet() throws Exception { if (!StringUtils.hasText(delegate.getTargetNamespace()) && typesProvider.getSchemaCollection() != null && typesProvider.getSchemaCollection().getXsdSchemas().length > 0) { XsdSchema schema = typesProvider.getSchemaCollection().getXsdSchemas()[0]; setTargetNamespace(schema.getTargetNamespace()); } if (!StringUtils.hasText(serviceName) && StringUtils.hasText(portTypesProvider.getPortTypeName())) { soapProvider.setServiceName(portTypesProvider.getPortTypeName() + "Service"); } delegate.afterPropertiesSet(); } @Override public Source getSource() { return delegate.getSource(); } } 

In addition, the DefaultWsdl11Definition method of the WebServiceConfig class would change as follows (to use the setting above):

 @Bean(name = "countries") public Wsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) { MyWsdl11Definition wsdl11Definition = new MyWsdl11Definition(); wsdl11Definition.setPortTypeName("CountriesPort"); wsdl11Definition.setLocationUri("/ws"); wsdl11Definition.setRequestSuffix(""); wsdl11Definition.setTargetNamespace("http://spring.io/guides/gs-producing-web-service"); wsdl11Definition.setSchema(countriesSchema); return wsdl11Definition; } 

Pay attention to wsdl11Definition.setRequestSuffix(""); which effectively sets the suffix to blank.

After this setting, you can change the XSD by removing the Request suffix, a new GetCountry class will be created, you will need to manually delete the previously existing GetCountryRequest and fix compilation errors as described above (test and endpoint class, just remember to update the @PayloadRoot annotation as well).

Build will work fine. By launching Application main, the created WSDL will contain as requested:

 <wsdl:binding name="CountriesPortSoap11" type="tns:CountriesPort"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="getCountry"> <soap:operation soapAction=""/> <wsdl:input name="getCountry"> <soap:body use="literal"/> </wsdl:input> <wsdl:output name="getCountryResponse"> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> 

Hope this helps. This is a real-world example of what a configuration agreement provides to a large extent, and that instead, a small, unexpected change in structure would mean in terms of writing code and adding settings.

+13
source

Spring-WS wsdl auto exposure function is based on conventions described in http://docs.spring.io/spring-ws/site/reference/html/server.html#server-automatic-wsdl-exposure

The tutorial you use as a starting point is using annotations instead of a namespace, but there should be a way to specify the requestSuffix and responseSuffix properties mentioned in the documentation. However, I am afraid that you cannot leave them empty.

Alternatively, you can set up a manually written WSDL. I suggest doing this since you got wsdl from the start.

+2
source

There is an easier way. Instead of DefaultWsdl11Definition use ProviderBasedWsdl4jDefinition and set all providers to default, and one instead of SuffixBasedPortTypesProvider use something like this:

 public class DelphiStylePortTypesProvider extends AbstractPortTypesProvider { @Override protected String getOperationName(Message msg) { if (isInputMessage(msg)) { return msg.getQName().getLocalPart(); } if (isOutputMessage(msg)) { return msg.getQName().getLocalPart().replace("Response", ""); } return ""; } @Override protected boolean isInputMessage(Message msg) { return !msg.getQName().getLocalPart().endsWith("Response"); } @Override protected boolean isOutputMessage(Message msg) { return msg.getQName().getLocalPart().endsWith("Response"); } @Override protected boolean isFaultMessage(Message msg) { return false; } } 
+1
source

I think the best solution is to write the WSDL file that you need. In my opinion, creating a custom WSDL from an XSD file with a request name and response name other than Spring Convention cannot be done. Therefore, it is better to create WSDL and import it into the project, and then generate your class from it.

0
source

Source: https://habr.com/ru/post/1239473/


All Articles