diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/AbstractXMLSchemaValidator.java b/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/AbstractXMLSchemaValidator.java index 7c4ebd77c8..9edd089da5 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/AbstractXMLSchemaValidator.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/AbstractXMLSchemaValidator.java @@ -14,28 +14,36 @@ package com.predic8.membrane.core.interceptor.schemavalidation; -import com.predic8.membrane.core.exchange.*; -import com.predic8.membrane.core.http.*; -import com.predic8.membrane.core.interceptor.*; -import com.predic8.membrane.core.multipart.*; -import com.predic8.membrane.core.resolver.*; -import com.predic8.membrane.core.util.*; -import org.jetbrains.annotations.*; -import org.slf4j.*; -import org.w3c.dom.*; -import org.xml.sax.*; - -import javax.xml.transform.*; -import javax.xml.transform.dom.*; -import javax.xml.validation.*; -import java.io.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; - -import static com.predic8.membrane.annot.Constants.*; -import static com.predic8.membrane.core.http.Header.*; -import static com.predic8.membrane.core.interceptor.Outcome.*; +import com.predic8.membrane.core.exchange.Exchange; +import com.predic8.membrane.core.http.Message; +import com.predic8.membrane.core.interceptor.Interceptor; +import com.predic8.membrane.core.interceptor.Outcome; +import com.predic8.membrane.core.multipart.XOPReconstitutor; +import com.predic8.membrane.core.resolver.ResolverMap; +import com.predic8.membrane.core.util.ConfigurationException; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import javax.xml.transform.Source; +import javax.xml.transform.dom.DOMSource; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.atomic.AtomicLong; + +import static com.predic8.membrane.annot.Constants.XSD_NS; +import static com.predic8.membrane.core.http.Header.VALIDATION_ERROR_SOURCE; +import static com.predic8.membrane.core.interceptor.Outcome.ABORT; +import static com.predic8.membrane.core.interceptor.Outcome.CONTINUE; public abstract class AbstractXMLSchemaValidator extends AbstractMessageValidator { @@ -78,7 +86,7 @@ public Outcome validateMessage(Exchange exc, Interceptor.Flow flow) throws Excep var exceptions = new ArrayList(); var preliminaryError = getPreliminaryError(xopr, msg); if (preliminaryError == null) { - List vals = validators.take(); + var vals = validators.take(); try { // the message must be valid for one schema embedded into WSDL for (var validator : vals) { diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/WSDLMessageElementExtractor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/WSDLMessageElementExtractor.java index 5eab122873..c11ecac8a2 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/WSDLMessageElementExtractor.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/WSDLMessageElementExtractor.java @@ -15,16 +15,18 @@ package com.predic8.membrane.core.interceptor.schemavalidation; import com.predic8.membrane.core.util.wsdl.parser.*; -import com.predic8.membrane.core.util.wsdl.parser.Operation.*; -import org.jetbrains.annotations.*; +import com.predic8.membrane.core.util.wsdl.parser.Operation.Direction; +import org.jetbrains.annotations.NotNull; -import javax.xml.namespace.*; +import javax.xml.namespace.QName; import java.util.*; -import java.util.stream.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; -import static com.predic8.membrane.core.util.wsdl.parser.Binding.Style.*; -import static com.predic8.membrane.core.util.wsdl.parser.Operation.Direction.*; -import static java.util.stream.Collectors.*; +import static com.predic8.membrane.core.util.wsdl.parser.Binding.Style.RPC; +import static com.predic8.membrane.core.util.wsdl.parser.Operation.Direction.INPUT; +import static com.predic8.membrane.core.util.wsdl.parser.Operation.Direction.OUTPUT; +import static java.util.stream.Collectors.toSet; public class WSDLMessageElementExtractor { @@ -108,7 +110,8 @@ private static String getElementNameRPC(Operation operation, Direction direction .flatMap(Collection::stream) .map(op -> op.getMessagesByDirection(direction)) .flatMap(Collection::stream) - .map(Message::getPart); + .map(Message::getPart) + .filter(Objects::nonNull); // Message can have no parts. } private record PortTypesByStyle(List portTypesRPC, List portTypesDocument) { diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/WSDLValidator.java b/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/WSDLValidator.java index 94adb8eb67..92fac850b1 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/WSDLValidator.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/WSDLValidator.java @@ -14,33 +14,38 @@ package com.predic8.membrane.core.interceptor.schemavalidation; -import com.predic8.membrane.core.exchange.*; -import com.predic8.membrane.core.http.*; +import com.predic8.membrane.core.exchange.Exchange; import com.predic8.membrane.core.http.Message; -import com.predic8.membrane.core.interceptor.*; -import com.predic8.membrane.core.multipart.*; -import com.predic8.membrane.core.resolver.*; -import com.predic8.membrane.core.util.*; -import com.predic8.membrane.core.util.wsdl.parser.Definitions.*; -import org.jetbrains.annotations.*; -import org.slf4j.*; -import org.w3c.dom.*; -import org.xml.sax.*; - -import javax.xml.namespace.*; -import javax.xml.parsers.*; -import javax.xml.transform.*; -import java.io.*; -import java.util.*; - -import static com.predic8.membrane.annot.Constants.SoapVersion.*; +import com.predic8.membrane.core.interceptor.Interceptor; +import com.predic8.membrane.core.interceptor.Outcome; +import com.predic8.membrane.core.multipart.XOPReconstitutor; +import com.predic8.membrane.core.resolver.ResolverMap; +import com.predic8.membrane.core.resolver.ResourceRetrievalException; +import com.predic8.membrane.core.util.ConfigurationException; +import com.predic8.membrane.core.util.MessageUtil; +import com.predic8.membrane.core.util.wsdl.parser.Definitions.SOAPVersion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import javax.xml.transform.Source; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.predic8.membrane.annot.Constants.SoapVersion.SOAP11; +import static com.predic8.membrane.annot.Constants.SoapVersion.SOAP12; import static com.predic8.membrane.core.http.Header.VALIDATION_ERROR_SOURCE; -import static com.predic8.membrane.core.interceptor.Outcome.*; -import static com.predic8.membrane.core.interceptor.schemavalidation.WSDLMessageElementExtractor.*; +import static com.predic8.membrane.core.interceptor.Outcome.ABORT; +import static com.predic8.membrane.core.interceptor.Outcome.CONTINUE; import static com.predic8.membrane.core.interceptor.schemavalidation.WSDLMessageElementExtractor.getPossibleRequestElements; -import static com.predic8.membrane.core.util.SOAPUtil.FaultCode.*; +import static com.predic8.membrane.core.interceptor.schemavalidation.WSDLMessageElementExtractor.getPossibleResponseElements; +import static com.predic8.membrane.core.util.SOAPUtil.FaultCode.Client; import static com.predic8.membrane.core.util.SOAPUtil.*; -import static com.predic8.membrane.core.util.wsdl.parser.Definitions.SOAPVersion.*; +import static com.predic8.membrane.core.util.wsdl.parser.Definitions.SOAPVersion.SOAP_11; +import static com.predic8.membrane.core.util.wsdl.parser.Definitions.SOAPVersion.SOAP_12; public class WSDLValidator extends AbstractXMLSchemaValidator { @@ -95,6 +100,12 @@ public String getName() { @Override public Outcome validateMessage(Exchange exc, Interceptor.Flow flow) throws Exception { var message = exc.getMessage(flow); + + if (flow == Interceptor.Flow.RESPONSE && message.isBodyEmpty() ) { + log.info("Skipping validation of empty response."); + return CONTINUE; + } + var result = analyseSOAPMessage(xopr, message); if (!result.isSOAP()) { @@ -119,11 +130,11 @@ public Outcome validateMessage(Exchange exc, Interceptor.Flow flow) throws Excep } } - if (message instanceof Request && !isPossibleRequestElement(result.soapElement())) { + if (flow == Interceptor.Flow.REQUEST && !isPossibleRequestElement(result.soapElement())) { setErrorResponse(exc, "%s is not a valid request element. Possible elements are %s".formatted(result.soapElement(), requestElements)); return ABORT; } - if (message instanceof Response && !result.isFault() && !isPossibleResponseElement(result.soapElement())) { + if (flow == Interceptor.Flow.RESPONSE && !result.isFault() && !isPossibleResponseElement(result.soapElement())) { setErrorResponse(exc, "%s is not a valid response element. Possible elements are %s".formatted(result.soapElement(), responseElements)); return ABORT; } diff --git a/core/src/main/java/com/predic8/membrane/core/util/wsdl/parser/Message.java b/core/src/main/java/com/predic8/membrane/core/util/wsdl/parser/Message.java index df4c4a4fae..9df232b6fb 100644 --- a/core/src/main/java/com/predic8/membrane/core/util/wsdl/parser/Message.java +++ b/core/src/main/java/com/predic8/membrane/core/util/wsdl/parser/Message.java @@ -14,9 +14,9 @@ package com.predic8.membrane.core.util.wsdl.parser; -import org.w3c.dom.*; +import org.w3c.dom.Node; -import java.util.*; +import java.util.List; public class Message extends WSDLElement { @@ -26,9 +26,12 @@ public Message(WSDLParserContext ctx, Node node) { /** * Document style only uses one part. - * @return + * @return first part or null if no part is defined. */ public Part getPart() { + if (getParts().isEmpty()) { + return null; + } return getParts().getFirst(); } diff --git a/core/src/test/java/com/predic8/membrane/core/util/soap/WSDLParserTest.java b/core/src/test/java/com/predic8/membrane/core/util/soap/WSDLParserTest.java index b3242a6a73..03455111ec 100644 --- a/core/src/test/java/com/predic8/membrane/core/util/soap/WSDLParserTest.java +++ b/core/src/test/java/com/predic8/membrane/core/util/soap/WSDLParserTest.java @@ -14,14 +14,15 @@ package com.predic8.membrane.core.util.soap; -import com.predic8.membrane.core.resolver.*; -import com.predic8.membrane.core.util.wsdl.parser.*; -import com.predic8.membrane.core.util.wsdl.parser.schema.*; -import org.junit.jupiter.api.*; +import com.predic8.membrane.core.resolver.ResolverMap; +import com.predic8.membrane.core.util.wsdl.parser.Definitions; +import com.predic8.membrane.core.util.wsdl.parser.schema.SchemaElement; +import org.junit.jupiter.api.Test; -import java.util.*; +import java.util.List; -import static com.predic8.membrane.core.util.wsdl.parser.Binding.Style.*; +import static com.predic8.membrane.core.util.wsdl.parser.Binding.Style.DOCUMENT; +import static com.predic8.membrane.core.util.wsdl.parser.Binding.Style.RPC; import static org.junit.jupiter.api.Assertions.*; class WSDLParserTest { @@ -194,4 +195,24 @@ void soap12() throws Exception { assertEquals("SayHelloResponse", definitions.getMessages().getLast().getName()); } + /** + * Message without a part + * + * + */ + @SuppressWarnings("OptionalGetWithoutIsPresent") + @Test + void emptyMessage() throws Exception { + var definitions = Definitions.parse(new ResolverMap(), "classpath:/ws/special/empty-message.wsdl"); + var schema = definitions.getSchemas().getFirst(); + var schemaElements = schema.getSchemaElements(); + var schemaElementNames = schemaElements.stream().map(SchemaElement::getName).toList(); + assertEquals(List.of("getCity"), schemaElementNames); + + assertEquals(2, definitions.getMessages().size()); + var msg = definitions.getMessages().stream().filter(m -> "CityResponse".equals(m.getName())).findFirst().get(); + assertEquals(0, msg.getParts().size()); + assertNull(msg.getPart()); + assertEquals("CityResponse", msg.getName()); + } } \ No newline at end of file diff --git a/core/src/test/resources/ws/special/empty-message.wsdl b/core/src/test/resources/ws/special/empty-message.wsdl new file mode 100644 index 0000000000..a5f898a75d --- /dev/null +++ b/core/src/test/resources/ws/special/empty-message.wsdl @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file