diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/soap/SampleSoapServiceInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/soap/SampleSoapServiceInterceptor.java
new file mode 100644
index 000000000..4f389f4ab
--- /dev/null
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/soap/SampleSoapServiceInterceptor.java
@@ -0,0 +1,212 @@
+package com.predic8.membrane.core.interceptor.soap;
+
+import com.predic8.membrane.annot.*;
+import com.predic8.membrane.core.exchange.*;
+import com.predic8.membrane.core.http.*;
+import com.predic8.membrane.core.interceptor.*;
+import org.w3c.dom.*;
+
+import javax.xml.parsers.*;
+import javax.xml.stream.*;
+import javax.xml.stream.events.*;
+import javax.xml.transform.*;
+import java.io.*;
+import java.util.*;
+import java.util.regex.Pattern;
+
+import static com.predic8.membrane.core.Constants.*;
+import static com.predic8.membrane.core.http.Header.CONTENT_TYPE;
+import static com.predic8.membrane.core.http.MimeType.*;
+import static com.predic8.membrane.core.http.Response.*;
+import static com.predic8.membrane.core.interceptor.Outcome.*;
+import static com.predic8.membrane.core.openapi.util.Utils.*;
+import static com.predic8.membrane.core.util.XMLUtil.*;
+import static javax.xml.stream.XMLStreamConstants.*;
+
+@MCElement(name = "sampleSoapService")
+public class SampleSoapServiceInterceptor extends AbstractInterceptor {
+
+ public static final Pattern WSDL_PATH_PARAM = Pattern.compile("(?i).+\\?.*wsdl.*");
+ public static final String CITY_SERVICE_NS = "https://predic8.de/city-service";
+
+ public SampleSoapServiceInterceptor() {
+ name = "SampleSoapService";
+ }
+
+ @Override
+ public Outcome handleRequest(Exchange exc) throws Exception {
+ if (isWSDLRequest(exc)) {
+ exc.setResponse(createWSDLResponse(exc));
+ } else if(!exc.getRequest().isPOSTRequest()) {
+ exc.setResponse(createMethodNotAllowedSOAPFault());
+ } else {
+ try {
+ exc.setResponse(createGetCityResponse(exc));
+ } catch (Exception e) {
+ exc.setResponse(createResourceNotFoundSOAPFault());
+ }
+ }
+ return RETURN;
+ }
+
+ private static Response createResourceNotFoundSOAPFault() throws Exception {
+ return ok(getSoapFault("Resource Not Found", "404", "Cannot parse SOAP message. Request should contain e.g. Bonn")).contentType(APPLICATION_XML).build();
+ }
+
+ private static Response createGetCityResponse(Exchange exc) throws Exception {
+ return ok(getResponse(getCity(exc))).contentType(TEXT_XML).build();
+ }
+
+ private static Response createMethodNotAllowedSOAPFault() throws Exception {
+ return ok(getSoapFault("Method Not Allowed", "405", "Use POST to access the service.")).contentType(APPLICATION_XML).build();
+ }
+
+ private Response createWSDLResponse(Exchange exc) throws XMLStreamException {
+ return ok().header(CONTENT_TYPE, TEXT_XML_UTF8)
+ .body(setWsdlServer(
+ getResourceAsStream(this,"/wsdl/city.wsdl"),exc)
+ ).build();
+ }
+
+
+ static boolean isWSDLRequest(Exchange exc) {
+ return WSDL_PATH_PARAM.matcher(exc.getRequest().getUri()).matches();
+ }
+
+ private static String getCity(Exchange exc) throws Exception {
+ return getElementAsString(exc.getRequest().getBodyAsStream(), "name");
+ }
+
+ private static final HashMap cityMap = new HashMap<>() {{
+ put("Bonn", new City("Bonn", 327_000, "Germany"));
+ put("Bielefeld", new City("Bielefeld", 333_000, "Germany"));
+ put("Manila", new City("Manila", 1_780_000, "Philippines"));
+ put("Da Nang", new City("Da Nang", 1_220_000, "Vietnam"));
+ put("London", new City("London", 8_980_000, "England"));
+ put("New York", new City("New York", 8_460_000, "USA"));
+ }};
+
+ public static String getSoapFault(String faultString, String code, String errorMessage) {
+ return """
+
+
+
+ s11:Client
+ %s
+
+ %s
+ %s
+
+
+
+
+ """.formatted(faultString, code, errorMessage);
+ }
+
+ public static String getElementAsString(InputStream is, String localName) throws Exception {
+ // MLInputFactory is not required to be thread-safe, ...
+ // https://javadoc.io/static/com.sun.xml.ws/jaxws-rt/2.2.10-b140319.1121/com/sun/xml/ws/api/streaming/XMLStreamReaderFactory.Default.html#:~:text=XMLInputFactory%20is%20not%20required%20to,using%20a%20XMLInputFactory%20per%20thread.
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+ XMLStreamReader reader = factory.createXMLStreamReader(is);
+
+ while (reader.hasNext()) {
+ if (reader.next() == START_ELEMENT) {
+ if (reader.getName().getLocalPart().equals(localName)) {
+ return reader.getElementText();
+ }
+ }
+ }
+ throw new Exception();
+ }
+
+ public static String setWsdlServer(InputStream is, Exchange exc) throws XMLStreamException {
+ // XMLEventFactory is not required to be thread-safe, ...
+ // https://javadoc.io/static/com.sun.xml.ws/jaxws-rt/2.2.10-b140319.1121/com/sun/xml/ws/api/streaming/XMLStreamReaderFactory.Default.html
+ StringWriter modifiedXmlWriter = new StringWriter();
+ XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(Objects.requireNonNull(is));
+ XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(modifiedXmlWriter);
+ XMLEventFactory fac = XMLEventFactory.newInstance();
+
+ while (reader.hasNext()) {
+ XMLEvent event = reader.nextEvent();
+ if (event.isStartElement()) {
+ StartElement startElement = event.asStartElement();
+ if ("address".equals(startElement.getName().getLocalPart())) {
+ writer.add(fac.createStartElement("soap", "soap", "address"));
+ writer.add(fac.createAttribute("location", getSOAPAddress(exc)));
+ } else {
+ writer.add(event);
+ }
+ } else {
+ writer.add(event);
+ }
+ }
+
+ return modifiedXmlWriter.toString();
+ }
+
+ public static String getSOAPAddress(Exchange exc) {
+ return exc.getInboundProtocol() + "://" + exc.getRequest().getHeader().getHost() + getPathWithoutParam(exc.getOriginalRequestUri());
+ }
+
+ static String getPathWithoutParam(String exc) {
+ return exc.replaceAll("\\?.*$", "");
+ }
+
+
+ public static String getResponse(String city) throws ParserConfigurationException, TransformerException {
+ try {
+ return xml2string(createResponse(city));
+ } catch (Exception e) {
+ return getSoapFault("Not Found", "404", "Do not know %s. Try Bonn, London or New York".formatted(city));
+ }
+ }
+
+ private static Document createResponse(String city) throws Exception {
+ // DocumentBuilderFactory is not guaranteed to be thread safe
+ // https://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/1.5/api/javax/xml/parsers/DocumentBuilderFactory.html
+ Document res = createDocumentBuilder().newDocument();
+ res.appendChild(createResponseEnvelope(city, res));
+ return res;
+ }
+
+ private static Element createResponseEnvelope(String city, Document res) {
+ Element env = res.createElementNS(SOAP11_NS, "s:Envelope");
+ env.setAttribute("xmlns:s", SOAP11_NS);
+ env.setAttribute("xmlns:cs", CITY_SERVICE_NS);
+ env.appendChild(createBody(city, res));
+ return env;
+ }
+
+ private static Element createBody(String city, Document res) {
+ Element body = res.createElement("s:Body");
+ body.appendChild(createCityDetails(city, res));
+ return body;
+ }
+
+ private static Element createCityDetails(String city, Document res) {
+ Element details = res.createElement("cs:getCityResponse");
+ details.appendChild(createCountry(city, res));
+ details.appendChild(createPopulation(city, res));
+ return details;
+ }
+
+ private static Element createPopulation(String city, Document res) {
+ Element pop = res.createElement("population");
+ pop.appendChild(res.createTextNode(String.valueOf(cityMap.get(city).population)));
+ return pop;
+ }
+
+ private static Element createCountry(String city, Document res) {
+ Element country = res.createElement("country");
+ country.appendChild(res.createTextNode(cityMap.get(city).country));
+ return country;
+ }
+
+ private static DocumentBuilder createDocumentBuilder() throws ParserConfigurationException {
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ }
+
+ public record City(String name, int population, String country) {}
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/xml/Xml2JsonInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/xml/Xml2JsonInterceptor.java
index a9a55e93b..f3af18991 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/xml/Xml2JsonInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/xml/Xml2JsonInterceptor.java
@@ -33,6 +33,7 @@
import static com.predic8.membrane.core.interceptor.Outcome.CONTINUE;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static javax.xml.transform.OutputKeys.*;
/**
@@ -92,10 +93,10 @@ public static String documentToString(Document doc) {
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
Transformer transformer = tf.newTransformer();
- transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
- transformer.setOutputProperty(OutputKeys.METHOD, "xml");
- transformer.setOutputProperty(OutputKeys.INDENT, "no");
- transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.setOutputProperty(OMIT_XML_DECLARATION, "no");
+ transformer.setOutputProperty(METHOD, "xml");
+ transformer.setOutputProperty(INDENT, "no");
+ transformer.setOutputProperty(ENCODING, "UTF-8");
transformer.transform(new DOMSource(doc), new StreamResult(sw));
return sw.toString();
} catch (Exception ex) {
diff --git a/core/src/main/java/com/predic8/membrane/core/util/SOAPUtil.java b/core/src/main/java/com/predic8/membrane/core/util/SOAPUtil.java
index 20532a532..43599d2c0 100644
--- a/core/src/main/java/com/predic8/membrane/core/util/SOAPUtil.java
+++ b/core/src/main/java/com/predic8/membrane/core/util/SOAPUtil.java
@@ -94,17 +94,12 @@ public static boolean isFault(XMLInputFactory xmlInputFactory, XOPReconstitutor
String expected;
switch (state) {
- case 0:
- expected = "Envelope";
- break;
- case 1:
- expected = "Body";
- break;
- case 2:
- expected = "Fault";
- break;
- default:
- return false;
+ case 0 -> expected = "Envelope";
+ case 1 -> expected = "Body";
+ case 2 -> expected = "Fault";
+ default -> {
+ return false;
+ }
}
if (expected.equals(name.getLocalPart())) {
if (state == 2)
diff --git a/core/src/main/java/com/predic8/membrane/core/util/XMLUtil.java b/core/src/main/java/com/predic8/membrane/core/util/XMLUtil.java
new file mode 100644
index 000000000..0151bcee7
--- /dev/null
+++ b/core/src/main/java/com/predic8/membrane/core/util/XMLUtil.java
@@ -0,0 +1,29 @@
+package com.predic8.membrane.core.util;
+
+import org.w3c.dom.*;
+
+import javax.xml.transform.*;
+import javax.xml.transform.dom.*;
+import javax.xml.transform.stream.*;
+import java.io.*;
+
+import static javax.xml.transform.OutputKeys.INDENT;
+import static javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION;
+
+public class XMLUtil {
+
+ // TODO 2. param boolean indent
+ // Write Test
+ public static String xml2string(Document doc) throws TransformerException {
+ TransformerFactory tfFactory = TransformerFactory.newInstance(); // Comment ThreadSafe? with URL
+ Transformer tf = tfFactory.newTransformer();
+ tf.setOutputProperty(OMIT_XML_DECLARATION, "yes");
+
+ tf.setOutputProperty(INDENT, "yes");
+ tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+
+ StringWriter writer = new StringWriter();
+ tf.transform(new DOMSource(doc), new StreamResult(writer));
+ return writer.toString();
+ }
+}
diff --git a/core/src/main/resources/wsdl/city.wsdl b/core/src/main/resources/wsdl/city.wsdl
new file mode 100644
index 000000000..55ad7a2f0
--- /dev/null
+++ b/core/src/main/resources/wsdl/city.wsdl
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/test/java/com/predic8/membrane/core/interceptor/soap/SampleSoapInterceptorTest.java b/core/src/test/java/com/predic8/membrane/core/interceptor/soap/SampleSoapInterceptorTest.java
new file mode 100644
index 000000000..0cb9e7694
--- /dev/null
+++ b/core/src/test/java/com/predic8/membrane/core/interceptor/soap/SampleSoapInterceptorTest.java
@@ -0,0 +1,179 @@
+package com.predic8.membrane.core.interceptor.soap;
+
+import com.predic8.membrane.core.exchange.Exchange;
+import com.predic8.membrane.core.http.MimeType;
+import com.predic8.membrane.core.http.Request;
+import com.predic8.membrane.core.rules.ServiceProxy;
+import com.predic8.membrane.core.rules.ServiceProxyKey;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.Objects;
+
+import static com.predic8.membrane.core.interceptor.soap.SampleSoapServiceInterceptor.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class SampleSoapInterceptorTest {
+
+ private static SampleSoapServiceInterceptor service;
+ private static final Exchange exc = new Exchange(null);
+
+ private static ServiceProxy serviceProxy;
+
+ @BeforeAll
+ public static void setUp() throws IOException {
+ service = new SampleSoapServiceInterceptor();
+ serviceProxy = new ServiceProxy(new ServiceProxyKey("localhost", ".*", ".*", 3011), "thomas-bayer.com", 80);
+ }
+
+ @Test
+ public void notFoundTest() throws Exception {
+ exc.setRequest(new Request.Builder().contentType(MimeType.TEXT_XML).post("/foo")
+ .body(IOUtils.toByteArray(Objects.requireNonNull(this.getClass().getResourceAsStream("/soap-sample/wrong-request.xml")))).build());
+ service.handleRequest(exc);
+ assertEquals(SampleSoapServiceInterceptor.getSoapFault("Resource Not Found", "404", "Cannot parse SOAP message. Request should contain e.g. Bonn"), exc.getResponse().getBody().toString());
+ // System.out.println(exc.getResponse().getBody().toString());
+ }
+
+ @ParameterizedTest
+ @CsvSource({
+ "/foo?bar, false",
+ "/foo?content-wsdl, true",
+ "/foo?wSdL, true",
+ "/foo?w-sdl, false"
+ })
+ public void isWsdlTest(String path, boolean expected) throws Exception {
+ exc.setRequest(new Request.Builder().get(path).build());
+ assertEquals(expected, isWSDLRequest(exc));
+ }
+
+ @Test
+ public void wsdlTest() throws Exception {
+ exc.setRequest(new Request.Builder().contentType(MimeType.TEXT_XML).get("/bar?wsdl").header("Host", "Host").build());
+ exc.setRule(serviceProxy);
+ exc.setOriginalRequestUri("/foo");
+ service.handleRequest(exc);
+ // System.out.println(exc.getResponse().getBody().toString());
+ assertTrue(exc.getResponse().getBody().toString().contains("Host"));
+ }
+
+ @ParameterizedTest
+ @CsvSource({
+ "/foo?bar, /foo",
+ "/foo?content-wsdl, /foo",
+ "/foo?wSdL, /foo",
+ "/foo?w-sdl, /foo"
+ })
+ public void getPathWithoutParamTest(String path, String expected) throws Exception {
+ assertEquals(getPathWithoutParam(path), expected);
+ }
+
+
+ @Test
+ public void methodTest() throws Exception {
+ exc.setRequest(new Request.Builder().contentType(MimeType.TEXT_XML).get("/foo").build());
+ service.handleRequest(exc);
+ assertTrue(exc.getResponse().getBodyAsStringDecoded().contains("Use POST to access the service."));
+ }
+
+
+ private void testValidRequest(String requestFileName, String country, String population) throws Exception {
+ InputStream requestStream = getClass().getResourceAsStream("/soap-sample/" + requestFileName);
+ exc.setRequest(new Request.Builder().contentType(MimeType.TEXT_XML).body(IOUtils.toByteArray(Objects.requireNonNull(requestStream))).post("/foo").build());
+ service.handleRequest(exc);
+
+ String responseXML = exc.getResponse().getBody().toString();
+ System.out.println(exc.getResponse().getBody().toString());
+
+ assertTrue(compareXmlStrings(responseXML, country, population));
+ }
+
+ @Test
+ public void validRequest1Test() throws Exception {
+ testValidRequest("soap-request-bonn.xml", "Germany", "327000");
+ }
+
+ @Test
+ public void validRequest2Test() throws Exception {
+ testValidRequest("soap-request-london.xml", "England", "8980000");
+ }
+
+ @Test
+ public void validRequest3Test() throws Exception {
+ testValidRequest("soap-request-new-york.xml", "USA", "8460000");
+ }
+
+ private boolean compareXmlStrings(String xml, String country, String population) {
+ try {
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(xml.getBytes());
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(new InputSource(inputStream));
+
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ xPath.setNamespaceContext(new NamespaceContext() {
+ @Override
+ public Iterator getPrefixes(String namespaceURI) {
+ return null;
+ }
+
+ @Override
+ public String getPrefix(String namespaceURI) {
+ if ("http://schemas.xmlsoap.org/soap/envelope/".equals(namespaceURI)) {
+ return "s";
+ } else if ("https://predic8.de/city-service".equals(namespaceURI)) {
+ return "cs";
+ }
+ return null;
+ }
+
+ @Override
+ public String getNamespaceURI(String prefix) {
+ if ("s".equals(prefix)) {
+ return "http://schemas.xmlsoap.org/soap/envelope/";
+ } else if ("cs".equals(prefix)) {
+ return "https://predic8.de/city-service";
+ }
+ return null;
+ }
+ });
+
+ Node countryNode = (Node) xPath.evaluate(
+ String.format("/s:Envelope/s:Body/cs:getCityResponse/country[text()='%s']", country),
+ document,
+ XPathConstants.NODE
+ );
+ Node populationNode = (Node) xPath.evaluate(
+ String.format("/s:Envelope/s:Body/cs:getCityResponse/population[text()='%s']", population),
+ document,
+ XPathConstants.NODE
+ );
+
+ return country.equals(countryNode != null ? countryNode.getTextContent() : null)
+ && population.equals(populationNode != null ? populationNode.getTextContent() : null);
+
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+}
diff --git a/core/src/test/resources/soap-sample/soap-request-bonn.xml b/core/src/test/resources/soap-sample/soap-request-bonn.xml
new file mode 100644
index 000000000..5b448a8b5
--- /dev/null
+++ b/core/src/test/resources/soap-sample/soap-request-bonn.xml
@@ -0,0 +1,7 @@
+
+
+
+ Bonn
+
+
+
\ No newline at end of file
diff --git a/core/src/test/resources/soap-sample/soap-request-london.xml b/core/src/test/resources/soap-sample/soap-request-london.xml
new file mode 100644
index 000000000..22bc7b8c1
--- /dev/null
+++ b/core/src/test/resources/soap-sample/soap-request-london.xml
@@ -0,0 +1,7 @@
+
+
+
+ London
+
+
+
\ No newline at end of file
diff --git a/core/src/test/resources/soap-sample/soap-request-new-york.xml b/core/src/test/resources/soap-sample/soap-request-new-york.xml
new file mode 100644
index 000000000..d26455aa4
--- /dev/null
+++ b/core/src/test/resources/soap-sample/soap-request-new-york.xml
@@ -0,0 +1,7 @@
+
+
+
+ New York
+
+
+
\ No newline at end of file
diff --git a/core/src/test/resources/soap-sample/wrong-request.xml b/core/src/test/resources/soap-sample/wrong-request.xml
new file mode 100644
index 000000000..3bcad09be
--- /dev/null
+++ b/core/src/test/resources/soap-sample/wrong-request.xml
@@ -0,0 +1,7 @@
+
+
+
+ Bonn
+
+
+
\ No newline at end of file
diff --git a/distribution/examples/soap/sample-soap-service/README.md b/distribution/examples/soap/sample-soap-service/README.md
new file mode 100644
index 000000000..858d0df10
--- /dev/null
+++ b/distribution/examples/soap/sample-soap-service/README.md
@@ -0,0 +1,33 @@
+# Sample Soap Service: CitiesService
+
+This plugin offers a sample SOAP service that can be used in tutorials and for testing purposes.
+
+## Starting the Service
+
+1. Go to the `examples/soap/sample-soap-service` directory.
+2. Run `service-proxy.sh` or `service-proxy.bat` to start the API Gateway.
+
+## Using the Service
+### Get the WSDL
+
+Unter `http://localhost:2000?wsdl` you can retrieve the Web Service Description Language (WSDL).
+
+`curl http://localhost:2000?wsdl`
+
+### Get City Information
+You can retrieve information about a city using the following curl:
+```
+curl http://localhost:2000 \
+-H 'Content-Type: text/xml' \
+-d '
+
+
+
+ Bonn
+
+
+'
+```
+Please note that this is an example service, and it contains data for the following cities:
+Bonn, Bielefeld, Manila, Da Nang, London, and New York. You can replace Bonn`
+in the above request with the city of your choice to retrieve information for a different city.
\ No newline at end of file
diff --git a/distribution/examples/soap/sample-soap-service/proxies.xml b/distribution/examples/soap/sample-soap-service/proxies.xml
new file mode 100644
index 000000000..aa0340097
--- /dev/null
+++ b/distribution/examples/soap/sample-soap-service/proxies.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/examples/soap/sample-soap-service/request.xml b/distribution/examples/soap/sample-soap-service/request.xml
new file mode 100644
index 000000000..5b448a8b5
--- /dev/null
+++ b/distribution/examples/soap/sample-soap-service/request.xml
@@ -0,0 +1,7 @@
+
+
+
+ Bonn
+
+
+
\ No newline at end of file
diff --git a/distribution/examples/soap/sample-soap-service/requests.http b/distribution/examples/soap/sample-soap-service/requests.http
new file mode 100644
index 000000000..a6827cf15
--- /dev/null
+++ b/distribution/examples/soap/sample-soap-service/requests.http
@@ -0,0 +1,14 @@
+GET http://localhost:2000?WSDL
+
+###
+
+POST http://localhost:2000
+Content-Type: text/xml
+
+
+
+
+ London
+
+
+
\ No newline at end of file
diff --git a/distribution/examples/soap/sample-soap-service/service-proxy.bat b/distribution/examples/soap/sample-soap-service/service-proxy.bat
new file mode 100644
index 000000000..6742f74b4
--- /dev/null
+++ b/distribution/examples/soap/sample-soap-service/service-proxy.bat
@@ -0,0 +1,18 @@
+@echo off
+if not "%MEMBRANE_HOME%" == "" goto homeSet
+set "MEMBRANE_HOME=%cd%\..\..\.."
+echo "%MEMBRANE_HOME%"
+if exist "%MEMBRANE_HOME%\service-proxy.bat" goto homeOk
+
+:homeSet
+if exist "%MEMBRANE_HOME%\service-proxy.bat" goto homeOk
+echo Please set the MEMBRANE_HOME environment variable to point to
+echo the directory where you have extracted the Membrane software.
+exit
+
+:homeOk
+set "CLASSPATH=%MEMBRANE_HOME%"
+set "CLASSPATH=%MEMBRANE_HOME%/conf"
+set "CLASSPATH=%CLASSPATH%;%MEMBRANE_HOME%/starter.jar"
+echo Membrane Router running...
+java -classpath "%CLASSPATH%" com.predic8.membrane.core.Starter -c proxies.xml
\ No newline at end of file
diff --git a/distribution/examples/soap/sample-soap-service/service-proxy.sh b/distribution/examples/soap/sample-soap-service/service-proxy.sh
new file mode 100755
index 000000000..2c6c5d0d6
--- /dev/null
+++ b/distribution/examples/soap/sample-soap-service/service-proxy.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+homeSet() {
+ echo "MEMBRANE_HOME variable is now set"
+ CLASSPATH="$MEMBRANE_HOME/conf"
+ CLASSPATH="$CLASSPATH:$MEMBRANE_HOME/starter.jar"
+ export CLASSPATH
+ echo Membrane Router running...
+ java -classpath "$CLASSPATH" com.predic8.membrane.core.Starter -c proxies.xml
+
+}
+
+terminate() {
+ echo "Starting of Membrane Router failed."
+ echo "Please execute this script from the appropriate subfolder of MEMBRANE_HOME/examples/"
+
+}
+
+homeNotSet() {
+ echo "MEMBRANE_HOME variable is not set"
+
+ if [ -f "`pwd`/../../../starter.jar" ]
+ then
+ export MEMBRANE_HOME="`pwd`/../../.."
+ homeSet
+ else
+ terminate
+ fi
+}
+
+
+if [ "$MEMBRANE_HOME" ]
+ then homeSet
+ else homeNotSet
+fi
+
diff --git a/distribution/src/test/java/com/predic8/membrane/examples/ExampleTestsWithoutInternet.java b/distribution/src/test/java/com/predic8/membrane/examples/ExampleTestsWithoutInternet.java
index a635b37cf..6f4244e6b 100644
--- a/distribution/src/test/java/com/predic8/membrane/examples/ExampleTestsWithoutInternet.java
+++ b/distribution/src/test/java/com/predic8/membrane/examples/ExampleTestsWithoutInternet.java
@@ -54,6 +54,7 @@
JSONSchemaValidationTest.class,
XMLValidationTest.class,
+ SampleSoapServiceTest.class,
// Transformatiom
Xml2JsonTest.class,
diff --git a/distribution/src/test/java/com/predic8/membrane/examples/tests/SampleSoapServiceTest.java b/distribution/src/test/java/com/predic8/membrane/examples/tests/SampleSoapServiceTest.java
new file mode 100644
index 000000000..cf791af05
--- /dev/null
+++ b/distribution/src/test/java/com/predic8/membrane/examples/tests/SampleSoapServiceTest.java
@@ -0,0 +1,68 @@
+package com.predic8.membrane.examples.tests;
+
+import com.predic8.membrane.examples.util.AbstractSampleMembraneStartStopTestcase;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.HashMap;
+import java.util.List;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.containsString;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class SampleSoapServiceTest extends AbstractSampleMembraneStartStopTestcase {
+
+ @Override
+ protected String getExampleDirName() {
+ return "soap/sampleSoapService";
+ }
+
+ private final HashMap methodGETmap = new HashMap<>() {{
+ String soapWsdlSubStr = "Method Not Allowed");
+
+ }};
+
+ final List parameters() {
+ return methodGETmap.keySet().stream().toList();
+ }
+
+ @ParameterizedTest
+ @MethodSource("parameters")
+ public void testMethod(String query) {
+ given()
+ .when()
+ .get("http://localhost:2000/" + query)
+ .then()
+ .body(containsString(methodGETmap.get(query)));
+
+ }
+
+
+ private final HashMap cityMap = new HashMap<>() {{
+ put("Bonn", "327000");
+ put("London", "8980000");
+ put("Berlin", "404");
+
+ }};
+
+ final List cityMap() {
+ return cityMap.keySet().stream().toList();
+ }
+
+ @ParameterizedTest
+ @MethodSource("cityMap")
+ public void testCity(String city) throws Exception {
+ given()
+ .body(readFileFromBaseDir("request.xml").replace("Bonn", city))
+ .when()
+ .post("http://localhost:2000/")
+ .then()
+ .body(containsString(cityMap.get(city)));
+
+ }
+}