SOAP Integration Issues
The assignment was clear: the WSDL is the contract. Don't modify it. Don't modify the mock server. If the platform misbehaves, the fix belongs entirely on your side.
The first problem was deceptively simple. Every request either returned a 404
or a mysterious NotFound for a CPE ID that absolutely existed. After exhausting the
obvious possibilities, I stopped debugging the application and started debugging the network.
Instead of pointing the SOAP client at the mock server, I temporarily pointed it at an HTTP echo container just to inspect the raw request.
@Bean
SoapClient soapClient() {
return new SoapClient("http://localhost:8080");
}became
@Bean
SoapClient soapClient() {
return new SoapClient("http://localhost:5678"); // http-echo
}Once I could see the raw HTTP exchange, two completely unrelated problems emerged.
The first was Apache CXF quietly attempting an HTTP/2 upgrade that the mock server couldn't understand.
requestContext.put(HTTP_VERSION_PROPERTY, "1.1");The second was even stranger. The mock server converts incoming XML into JSON before matching response templates. During that conversion it silently discarded XML namespace prefixes, while its own templates still expected them.
Incoming request:
<soapenv:Body>
<ser:getCPEById>
<ser:cpeid>123</ser:cpeid>
</ser:getCPEById>
</soapenv:Body>Internal representation:
{
"Body": {
"getCPEById": {
"cpeid": "123"
}
}
}Template matcher:
"matchesJsonPath": "$.soapenv:Body.ser:getCPEById.ser:cpeid"Those paths could never match. Neither issue had anything to do with the other. Both produced exactly the same symptom.
Once GET worked, PUT immediately failed.
Illegal processing instruction target ("xml")Every log showed perfectly valid XML.
The actual response, however, looked like this:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope>...Those invisible bytes before the XML declaration violate the XML specification. Most tools never show them, but a strict parser does.
I intercepted the raw stream before parsing instead of trying to fix the XML itself.
final class XmlNormalizingInterceptor extends AbstractPhaseInterceptor<Message> {
@Override
public void handleMessage(Message message) {
// strip leading whitespace before the XML declaration.
}
}The next issue was with SOAP faults.
The mock server correctly returns a fault.
<soap:Fault>
<faultcode>tns:NotFound</faultcode>
<faultstring>CPE not found</faultstring>
</soap:Fault>The problem is that the WSDL never declares it.
No wsdl:fault means Apache CXF cannot deserialize the fault,
so instead of receiving something like:
catch (NotFoundFault e) {
...
}everything arrives wrapped inside:
catch (WebServiceException e) {
...
}The only useful piece of information that survives is the fault code embedded in the exception message.
So the decoder ended up doing something I never expected to write.
for (var code : SoapFaultCode.values()) {
if (message.contains(code.value())) {
return Optional.of(code);
}
}I was scanning an exception message for a recognizable substring because the service contract wasn't allowed to describe its own errors.
Finally, there was one issue that wasn't really a bug. The OpenAPI specification describes the Wi-Fi password as optional.
The example 400 response strongly suggests it's mandatory for WPA2_PSK.
So I ended up reading about WEP, WPA2-PSK, WPA3-SAE, Enterprise authentication, and which encryption modes actually require a shared password before implementing validation based on how Wi-Fi works rather than what the specification said.
By the end, every issue had its own dated note: observations, experiments, conclusions, and references.
I also strongly suspect at least one of these issues was placed intentionally, to see whether it would be caught. Will give an update if I ever get a change to speak with the reviewer.