diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/soap/SampleSoapService.java b/core/src/main/java/com/predic8/membrane/core/interceptor/soap/SampleSoapService.java deleted file mode 100644 index f98f1a606..000000000 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/soap/SampleSoapService.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.predic8.membrane.core.interceptor.soap; - -import com.predic8.membrane.annot.MCElement; -import com.predic8.membrane.core.exchange.Exchange; -import com.predic8.membrane.core.http.Response; -import com.predic8.membrane.core.interceptor.AbstractInterceptor; -import com.predic8.membrane.core.interceptor.Outcome; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import javax.xml.namespace.QName; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import java.io.InputStream; -import java.io.StringWriter; - -import static com.predic8.membrane.core.interceptor.Outcome.CONTINUE; -import static com.predic8.membrane.core.interceptor.Outcome.RETURN; -import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; - -@MCElement(name="sampleSoapService") -public class SampleSoapService extends AbstractInterceptor { - - public SampleSoapService() { - name = "SampleSoapService"; - } - - @Override - public Outcome handleRequest(Exchange exc) throws Exception { - try { - String cityName = getElementAsString(exc.getRequest().getBodyAsStream(), "city"); - exc.setResponse(Response.ok(getResponse(cityName)).header("Content-Type", "application/xml").build()); - } catch (Exception e) { - exc.setResponse(Response.ok(getSoapFault("city element not found")).header("Content-Type", "application/xml").build()); - } - return RETURN; - } - - public static String getSoapFault(String error) { - return "\n" + - " \n" + - " \n" + - " soapenv:Client\n" + - " Resource Not Found\n" + - " \n" + - " 404\n" + - " " + error + "\n" + - " \n" + - " \n" + - " \n" + - ""; - } - - public static String getElementAsString(InputStream is, String localName) throws Exception { - XMLInputFactory factory = XMLInputFactory.newInstance(); - XMLStreamReader reader = factory.createXMLStreamReader(is); - - while (reader.hasNext()) { - int event = reader.next(); - if (event == START_ELEMENT) { - QName startElementName = reader.getName(); - if (startElementName.getLocalPart().equals(localName)) { - return reader.getElementText(); - } - } - } - throw new Exception(); - } - - public static String getResponse(String result) throws ParserConfigurationException, TransformerException { - if(result.equals("Bonn") || result.equals("London") || result.equals("New York")) { - // 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 - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document responseDoc = builder.newDocument(); - Element envElement = responseDoc.createElementNS("http://schemas.xmlsoap.org/soap/envelope/", "s:Envelope"); - envElement.setAttribute("xmlns:s", "http://schemas.xmlsoap.org/soap/envelope/"); - envElement.setAttribute("xmlns:cs", "https://predic8.de/city-service"); - Element body = responseDoc.createElement("s:Body"); - Element cityDetailsRes = responseDoc.createElement("cs:cityDetails"); - Element country = responseDoc.createElement("cs:country"); - Element population = responseDoc.createElement("cs:population"); - country.appendChild(responseDoc.createTextNode(getCountry(result))); - population.appendChild(responseDoc.createTextNode(String.valueOf(getPopulation(result)))); - cityDetailsRes.appendChild(country); - cityDetailsRes.appendChild(population); - body.appendChild(cityDetailsRes); - envElement.appendChild(body); - responseDoc.appendChild(envElement); - return getString(responseDoc); - }else{ - return getSoapFault("No city data available. Try Bonn, London or new York"); - } - } - - private static String getString(Document responseDoc) throws TransformerException { - TransformerFactory tfFactory = TransformerFactory.newInstance(); - Transformer tf = tfFactory.newTransformer(); - tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - tf.setOutputProperty(OutputKeys.INDENT, "yes"); - tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - DOMSource source = new DOMSource(responseDoc); - StringWriter writer = new StringWriter(); - StreamResult reswr = new StreamResult(writer); - tf.transform(source, reswr); - return writer.toString(); - } - - private static int getPopulation(String city) { - return switch (city) { - case "Bonn" -> 84000000; - case "London" -> 56000000; - case "New York" -> 332000000; - default -> 0; - }; - } - - private static String getCountry(String city) { - return switch (city) { - case "Bonn" -> "Germany"; - case "London" -> "England"; - case "New York" -> "USA"; - default -> "Unknown"; - }; - } -} \ No newline at end of file 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..ba9cb0055 --- /dev/null +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/soap/SampleSoapServiceInterceptor.java @@ -0,0 +1,151 @@ +package com.predic8.membrane.core.interceptor.soap; + +import com.predic8.membrane.annot.*; +import com.predic8.membrane.core.exchange.*; +import com.predic8.membrane.core.interceptor.*; +import org.w3c.dom.*; + +import javax.xml.parsers.*; +import javax.xml.stream.*; +import javax.xml.transform.*; +import java.io.*; + +import static com.predic8.membrane.core.Constants.*; +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.util.XMLUtil.*; +import static javax.xml.stream.XMLStreamConstants.*; + +@MCElement(name = "sampleSoapService") +public class SampleSoapServiceInterceptor extends AbstractInterceptor { + + public static final String CITY_SERVICE_NS = "https://predic8.de/city-service"; + + public SampleSoapServiceInterceptor() { + name = "SampleSoapService"; + } + + @Override + public Outcome handleRequest(Exchange exc) throws Exception { + try { + exc.setResponse(ok(getResponse(getCity(exc))).contentType(APPLICATION_XML).build()); + } catch (Exception e) { + exc.setResponse(ok(getSoapFault("Cannot parse SOAP message")).contentType(APPLICATION_XML).build()); + } + return RETURN; + } + + private static String getCity(Exchange exc) throws Exception { + return getElementAsString(exc.getRequest().getBodyAsStream(), "city"); + } + + public static String getSoapFault(String error) { + // Multiline String """ ... """ + // soapenv => s11 + return "\n" + + " \n" + + " \n" + + " soapenv:Client\n" + + " Resource Not Found\n" + + " \n" + + " 404\n" + + " " + error + "\n" + + " \n" + + " \n" + + " \n" + + ""; + } + + + // Test: Make String => InputStream + public static String getElementAsString(InputStream is, String localName) throws Exception { + XMLInputFactory factory = XMLInputFactory.newInstance(); // TODO Comment about Threadsafe + 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 getResponse(String city) throws ParserConfigurationException, TransformerException { + try { + return xml2string(createResponse(city)); + } catch (Exception e) { + return getSoapFault("Do not know %s. Try Bonn, London or new York".formatted(city)); // Todo abgleichen + } + } + + 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) throws Exception { + 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) throws Exception { + Element body = res.createElement("s:Body"); + body.appendChild(createCityDetails(city, res)); + return body; + } + + private static Element createCityDetails(String city, Document res) throws Exception { + Element details = res.createElement("cs:cityDetails"); + details.appendChild(createCountry(city, res)); + details.appendChild(createPopulation(city, res)); + return details; + } + + private static Element createPopulation(String city, Document res) throws Exception { + Element pop = res.createElement("cs:population"); + pop.appendChild(res.createTextNode(String.valueOf(getPopulation(city)))); + return pop; + } + + private static Element createCountry(String city, Document res) { + Element country = res.createElement("cs:country"); + country.appendChild(res.createTextNode(getCountry(city))); + return country; + } + + private static DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { + return DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } + + // Todo Put cities in Map + // record City(Name,Pop,Country) + private static int getPopulation(String city) throws Exception { + return switch (city) { + case "Bonn" -> 300_000; + case "Manila" -> 1000; + case "Da Nang" -> 1100; + case "Bielefeld" -> 1222; + case "London" -> 56_000_000; + case "New York" -> 332000000; + default -> throw new Exception("What city?"); + }; + } + + private static String getCountry(String city) { + return switch (city) { + case "Bonn" -> "Germany"; + case "London" -> "England"; + case "New York" -> "USA"; + default -> "Unknown"; + }; + } +} \ 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/test/java/com/predic8/membrane/core/interceptor/soap/SampleSoapInterceptorTest.java b/core/src/test/java/com/predic8/membrane/core/interceptor/soap/SampleSoapInterceptorTest.java index a85c22429..7a12e4341 100644 --- 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 @@ -7,9 +7,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import javax.xml.namespace.NamespaceContext; @@ -29,11 +27,11 @@ public class SampleSoapInterceptorTest { - private static SampleSoapService service; + private static SampleSoapServiceInterceptor service; private static Exchange exc = new Exchange(null); @BeforeAll public static void setUp() throws IOException { - service = new SampleSoapService(); + service = new SampleSoapServiceInterceptor(); } @Test @@ -41,7 +39,7 @@ public void notFoundTest() throws Exception { exc.setRequest(new Request.Builder().contentType(MimeType.TEXT_XML) .body(IOUtils.toByteArray(Objects.requireNonNull(this.getClass().getResourceAsStream("/soap-sample/wrongRequest.xml")))).build()); service.handleRequest(exc); - assertEquals(SampleSoapService.getSoapFault("city element not found"), exc.getResponse().getBody().toString()); + assertEquals(SampleSoapServiceInterceptor.getSoapFault("city element not found"), exc.getResponse().getBody().toString()); // System.out.println(exc.getResponse().getBody().toString()); }