pilak
6 years ago
committed by
Marvin Froeder
15 changed files with 1200 additions and 27 deletions
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
SOAP Codec |
||||
=================== |
||||
|
||||
This module adds support for encoding and decoding SOAP Body objects via JAXB and SOAPMessage. It also provides SOAPFault decoding capabilities by wrapping them into the original `javax.xml.ws.soap.SOAPFaultException`, so that you'll only need to catch `SOAPFaultException` in order to handle SOAPFault. |
||||
|
||||
Add `SOAPEncoder` and/or `SOAPDecoder` to your `Feign.Builder` like so: |
||||
|
||||
```java |
||||
public interface MyApi { |
||||
|
||||
@RequestLine("POST /getObject") |
||||
@Headers({ |
||||
"SOAPAction: getObject", |
||||
"Content-Type: text/xml" |
||||
}) |
||||
MyJaxbObjectResponse getObject(MyJaxbObjectRequest request); |
||||
|
||||
} |
||||
|
||||
... |
||||
|
||||
JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder() |
||||
.withMarshallerJAXBEncoding("UTF-8") |
||||
.withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd") |
||||
.build(); |
||||
|
||||
api = Feign.builder() |
||||
.encoder(new SOAPEncoder(jaxbFactory)) |
||||
.decoder(new SOAPDecoder(jaxbFactory)) |
||||
.target(MyApi.class, "http://api"); |
||||
|
||||
... |
||||
|
||||
try { |
||||
api.getObject(new MyJaxbObjectRequest()); |
||||
} catch (SOAPFaultException faultException) { |
||||
log.info(faultException.getFault().getFaultString()); |
||||
} |
||||
|
||||
``` |
||||
|
||||
Because a SOAP Fault can be returned as well with a 200 http code than a 4xx or 5xx HTTP error code (depending on the used API), you may also use `SOAPErrorDecoder` in your API configuration, in order to be able to catch `SOAPFaultException` in case of SOAP Fault. Add it, like below: |
||||
|
||||
```java |
||||
api = Feign.builder() |
||||
.encoder(new SOAPEncoder(jaxbFactory)) |
||||
.decoder(new SOAPDecoder(jaxbFactory)) |
||||
.errorDecoder(new SOAPErrorDecoder()) |
||||
.target(MyApi.class, "http://api"); |
||||
``` |
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!-- |
||||
|
||||
Copyright 2012-2018 The Feign Authors |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
||||
in compliance with the License. You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License |
||||
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
||||
or implied. See the License for the specific language governing permissions and limitations under |
||||
the License. |
||||
|
||||
--> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<parent> |
||||
<groupId>io.github.openfeign</groupId> |
||||
<artifactId>parent</artifactId> |
||||
<version>10.0.2-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<artifactId>feign-soap</artifactId> |
||||
<name>Feign SOAP</name> |
||||
<description>Feign SOAP CoDec</description> |
||||
|
||||
<properties> |
||||
<main.basedir>${project.basedir}/..</main.basedir> |
||||
</properties> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>${project.groupId}</groupId> |
||||
<artifactId>feign-core</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>${project.groupId}</groupId> |
||||
<artifactId>feign-jaxb</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>${project.groupId}</groupId> |
||||
<artifactId>feign-core</artifactId> |
||||
<type>test-jar</type> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
<profiles> |
||||
<profile> |
||||
<activation> |
||||
<jdk>11</jdk> |
||||
</activation> |
||||
|
||||
<!-- |
||||
JAX-WS was removed from java SDK on JEP 320 |
||||
http://openjdk.java.net/jeps/320 |
||||
--> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>javax.xml.bind</groupId> |
||||
<artifactId>jaxb-api</artifactId> |
||||
<version>2.3.1</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.glassfish.jaxb</groupId> |
||||
<artifactId>jaxb-runtime</artifactId> |
||||
<version>2.4.0-b180830.0438</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>javax.xml.ws</groupId> |
||||
<artifactId>jaxws-api</artifactId> |
||||
<version>2.3.1</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>com.sun.xml.messaging.saaj</groupId> |
||||
<artifactId>saaj-impl</artifactId> |
||||
<version>1.5.0</version> |
||||
</dependency> |
||||
</dependencies> |
||||
</profile> |
||||
</profiles> |
||||
|
||||
</project> |
@ -0,0 +1,168 @@
@@ -0,0 +1,168 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign Authors |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
||||
* in compliance with the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License |
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
||||
* or implied. See the License for the specific language governing permissions and limitations under |
||||
* the License. |
||||
*/ |
||||
package feign.soap; |
||||
|
||||
import java.io.IOException; |
||||
import java.lang.reflect.ParameterizedType; |
||||
import java.lang.reflect.Type; |
||||
import javax.xml.bind.JAXBException; |
||||
import javax.xml.soap.MessageFactory; |
||||
import javax.xml.soap.SOAPConstants; |
||||
import javax.xml.soap.SOAPException; |
||||
import javax.xml.soap.SOAPMessage; |
||||
import javax.xml.ws.soap.SOAPFaultException; |
||||
import feign.Response; |
||||
import feign.Util; |
||||
import feign.codec.DecodeException; |
||||
import feign.codec.Decoder; |
||||
import feign.jaxb.JAXBContextFactory; |
||||
|
||||
/** |
||||
* Decodes SOAP responses using SOAPMessage and JAXB for the body part. <br> |
||||
* |
||||
* <p> |
||||
* The JAXBContextFactory should be reused across requests as it caches the created JAXB contexts. |
||||
* </p> |
||||
* |
||||
* <p> |
||||
* A SOAP Fault can be returned with a 200 HTTP code. Hence, faults could be handled with no error |
||||
* on the HTTP layer. In this case, you'll certainly have to catch {@link SOAPFaultException} to get |
||||
* fault from your API client service. In the other case (Faults are returned with 4xx or 5xx HTTP |
||||
* error code), you may use {@link SOAPErrorDecoder} in your API configuration. |
||||
* |
||||
* <pre> |
||||
* |
||||
* public interface MyApi { |
||||
* |
||||
* @RequestLine("POST /getObject") |
||||
* @Headers({ |
||||
* "SOAPAction: getObject", |
||||
* "Content-Type: text/xml" |
||||
* }) |
||||
* MyJaxbObjectResponse getObject(MyJaxbObjectRequest request); |
||||
* |
||||
* } |
||||
* |
||||
* ... |
||||
* |
||||
* JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder() |
||||
* .withMarshallerJAXBEncoding("UTF-8") |
||||
* .withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd")
|
||||
* .build(); |
||||
* |
||||
* api = Feign.builder() |
||||
* .decoder(new SOAPDecoder(jaxbFactory)) |
||||
* .target(MyApi.class, "http://api");
|
||||
* |
||||
* ... |
||||
* |
||||
* try { |
||||
* api.getObject(new MyJaxbObjectRequest()); |
||||
* } catch (SOAPFaultException faultException) { |
||||
* log.info(faultException.getFault().getFaultString()); |
||||
* } |
||||
* </pre> |
||||
* |
||||
* </p> |
||||
* |
||||
* @see SOAPErrorDecoder |
||||
* @see SOAPFaultException |
||||
*/ |
||||
public class SOAPDecoder implements Decoder { |
||||
|
||||
|
||||
private final JAXBContextFactory jaxbContextFactory; |
||||
private final String soapProtocol; |
||||
|
||||
public SOAPDecoder(JAXBContextFactory jaxbContextFactory) { |
||||
this.jaxbContextFactory = jaxbContextFactory; |
||||
this.soapProtocol = SOAPConstants.DEFAULT_SOAP_PROTOCOL; |
||||
} |
||||
|
||||
private SOAPDecoder(Builder builder) { |
||||
this.soapProtocol = builder.soapProtocol; |
||||
this.jaxbContextFactory = builder.jaxbContextFactory; |
||||
} |
||||
|
||||
@Override |
||||
public Object decode(Response response, Type type) throws IOException { |
||||
if (response.status() == 404) |
||||
return Util.emptyValueOf(type); |
||||
if (response.body() == null) |
||||
return null; |
||||
while (type instanceof ParameterizedType) { |
||||
ParameterizedType ptype = (ParameterizedType) type; |
||||
type = ptype.getRawType(); |
||||
} |
||||
if (!(type instanceof Class)) { |
||||
throw new UnsupportedOperationException( |
||||
"SOAP only supports decoding raw types. Found " + type); |
||||
} |
||||
|
||||
try { |
||||
SOAPMessage message = |
||||
MessageFactory.newInstance(soapProtocol).createMessage(null, |
||||
response.body().asInputStream()); |
||||
if (message.getSOAPBody() != null) { |
||||
if (message.getSOAPBody().hasFault()) { |
||||
throw new SOAPFaultException(message.getSOAPBody().getFault()); |
||||
} |
||||
|
||||
return jaxbContextFactory.createUnmarshaller((Class<?>) type) |
||||
.unmarshal(message.getSOAPBody().extractContentAsDocument()); |
||||
} |
||||
} catch (SOAPException | JAXBException e) { |
||||
throw new DecodeException(e.toString(), e); |
||||
} finally { |
||||
if (response.body() != null) { |
||||
response.body().close(); |
||||
} |
||||
} |
||||
return Util.emptyValueOf(type); |
||||
|
||||
} |
||||
|
||||
|
||||
public static class Builder { |
||||
String soapProtocol = SOAPConstants.DEFAULT_SOAP_PROTOCOL; |
||||
JAXBContextFactory jaxbContextFactory; |
||||
|
||||
public Builder withJAXBContextFactory(JAXBContextFactory jaxbContextFactory) { |
||||
this.jaxbContextFactory = jaxbContextFactory; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* The protocol used to create message factory. Default is "SOAP 1.1 Protocol". |
||||
* |
||||
* @param soapProtocol a string constant representing the MessageFactory protocol. |
||||
* @see SOAPConstants#SOAP_1_1_PROTOCOL |
||||
* @see SOAPConstants#SOAP_1_2_PROTOCOL |
||||
* @see SOAPConstants#DYNAMIC_SOAP_PROTOCOL |
||||
* @see MessageFactory#newInstance(String) |
||||
*/ |
||||
public Builder withSOAPProtocol(String soapProtocol) { |
||||
this.soapProtocol = soapProtocol; |
||||
return this; |
||||
} |
||||
|
||||
public SOAPDecoder build() { |
||||
if (jaxbContextFactory == null) { |
||||
throw new IllegalStateException("JAXBContextFactory must be non-null"); |
||||
} |
||||
return new SOAPDecoder(this); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,199 @@
@@ -0,0 +1,199 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign Authors |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
||||
* in compliance with the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License |
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
||||
* or implied. See the License for the specific language governing permissions and limitations under |
||||
* the License. |
||||
*/ |
||||
package feign.soap; |
||||
|
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.IOException; |
||||
import java.lang.reflect.Type; |
||||
import java.nio.charset.Charset; |
||||
import javax.xml.bind.JAXBException; |
||||
import javax.xml.bind.Marshaller; |
||||
import javax.xml.parsers.DocumentBuilderFactory; |
||||
import javax.xml.parsers.ParserConfigurationException; |
||||
import javax.xml.soap.MessageFactory; |
||||
import javax.xml.soap.SOAPConstants; |
||||
import javax.xml.soap.SOAPException; |
||||
import javax.xml.soap.SOAPMessage; |
||||
import javax.xml.transform.OutputKeys; |
||||
import javax.xml.transform.Transformer; |
||||
import javax.xml.transform.TransformerException; |
||||
import javax.xml.transform.TransformerFactory; |
||||
import javax.xml.transform.TransformerFactoryConfigurationError; |
||||
import javax.xml.transform.dom.DOMSource; |
||||
import javax.xml.transform.stream.StreamResult; |
||||
import org.w3c.dom.Document; |
||||
import feign.RequestTemplate; |
||||
import feign.codec.EncodeException; |
||||
import feign.codec.Encoder; |
||||
import feign.jaxb.JAXBContextFactory; |
||||
|
||||
|
||||
/** |
||||
* Encodes requests using SOAPMessage and JAXB for the body part. <br> |
||||
* <p> |
||||
* Basic example with with Feign.Builder: |
||||
* </p> |
||||
* |
||||
* <pre> |
||||
* |
||||
* public interface MyApi { |
||||
* |
||||
* @RequestLine("POST /getObject") |
||||
* @Headers({ |
||||
* "SOAPAction: getObject", |
||||
* "Content-Type: text/xml" |
||||
* }) |
||||
* MyJaxbObjectResponse getObject(MyJaxbObjectRequest request); |
||||
* |
||||
* } |
||||
* |
||||
* ... |
||||
* |
||||
* JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder() |
||||
* .withMarshallerJAXBEncoding("UTF-8") |
||||
* .withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd") |
||||
* .build(); |
||||
* |
||||
* api = Feign.builder() |
||||
* .encoder(new SOAPEncoder(jaxbFactory)) |
||||
* .target(MyApi.class, "http://api"); |
||||
* |
||||
* ... |
||||
* |
||||
* try { |
||||
* api.getObject(new MyJaxbObjectRequest()); |
||||
* } catch (SOAPFaultException faultException) { |
||||
* log.info(faultException.getFault().getFaultString()); |
||||
* } |
||||
* </pre> |
||||
* |
||||
* <p> |
||||
* The JAXBContextFactory should be reused across requests as it caches the created JAXB contexts. |
||||
* </p> |
||||
*/ |
||||
public class SOAPEncoder implements Encoder { |
||||
|
||||
private static final String DEFAULT_SOAP_PROTOCOL = SOAPConstants.SOAP_1_1_PROTOCOL; |
||||
|
||||
private final boolean writeXmlDeclaration; |
||||
private final boolean formattedOutput; |
||||
private final Charset charsetEncoding; |
||||
private final JAXBContextFactory jaxbContextFactory; |
||||
private final String soapProtocol; |
||||
|
||||
private SOAPEncoder(Builder builder) { |
||||
this.jaxbContextFactory = builder.jaxbContextFactory; |
||||
this.writeXmlDeclaration = builder.writeXmlDeclaration; |
||||
this.charsetEncoding = builder.charsetEncoding; |
||||
this.soapProtocol = builder.soapProtocol; |
||||
this.formattedOutput = builder.formattedOutput; |
||||
} |
||||
|
||||
public SOAPEncoder(JAXBContextFactory jaxbContextFactory) { |
||||
this.jaxbContextFactory = jaxbContextFactory; |
||||
this.writeXmlDeclaration = true; |
||||
this.formattedOutput = false; |
||||
this.charsetEncoding = Charset.defaultCharset(); |
||||
this.soapProtocol = DEFAULT_SOAP_PROTOCOL; |
||||
} |
||||
|
||||
@Override |
||||
public void encode(Object object, Type bodyType, RequestTemplate template) { |
||||
if (!(bodyType instanceof Class)) { |
||||
throw new UnsupportedOperationException( |
||||
"SOAP only supports encoding raw types. Found " + bodyType); |
||||
} |
||||
try { |
||||
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); |
||||
Marshaller marshaller = jaxbContextFactory.createMarshaller((Class<?>) bodyType); |
||||
marshaller.marshal(object, document); |
||||
SOAPMessage soapMessage = MessageFactory.newInstance(soapProtocol).createMessage(); |
||||
soapMessage.setProperty(SOAPMessage.WRITE_XML_DECLARATION, |
||||
Boolean.toString(writeXmlDeclaration)); |
||||
soapMessage.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, charsetEncoding.displayName()); |
||||
soapMessage.getSOAPBody().addDocument(document); |
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
||||
if (formattedOutput) { |
||||
Transformer t = TransformerFactory.newInstance().newTransformer(); |
||||
t.setOutputProperty(OutputKeys.INDENT, "yes"); |
||||
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); |
||||
t.transform(new DOMSource(soapMessage.getSOAPPart()), new StreamResult(bos)); |
||||
} else { |
||||
soapMessage.writeTo(bos); |
||||
} |
||||
template.body(new String(bos.toByteArray())); |
||||
} catch (SOAPException | JAXBException | ParserConfigurationException | IOException |
||||
| TransformerFactoryConfigurationError | TransformerException e) { |
||||
throw new EncodeException(e.toString(), e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates instances of {@link SOAPEncoder}. |
||||
*/ |
||||
public static class Builder { |
||||
|
||||
private JAXBContextFactory jaxbContextFactory; |
||||
public boolean formattedOutput = false; |
||||
private boolean writeXmlDeclaration = true; |
||||
private Charset charsetEncoding = Charset.defaultCharset(); |
||||
private String soapProtocol = DEFAULT_SOAP_PROTOCOL; |
||||
|
||||
/** The {@link JAXBContextFactory} for body part. */ |
||||
public Builder withJAXBContextFactory(JAXBContextFactory jaxbContextFactory) { |
||||
this.jaxbContextFactory = jaxbContextFactory; |
||||
return this; |
||||
} |
||||
|
||||
/** Output format indent if true. Default is false */ |
||||
public Builder withFormattedOutput(boolean formattedOutput) { |
||||
this.formattedOutput = formattedOutput; |
||||
return this; |
||||
} |
||||
|
||||
/** Write the xml declaration if true. Default is true */ |
||||
public Builder withWriteXmlDeclaration(boolean writeXmlDeclaration) { |
||||
this.writeXmlDeclaration = writeXmlDeclaration; |
||||
return this; |
||||
} |
||||
|
||||
/** Specify the charset encoding. Default is {@link Charset#defaultCharset()}. */ |
||||
public Builder withCharsetEncoding(Charset charsetEncoding) { |
||||
this.charsetEncoding = charsetEncoding; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* The protocol used to create message factory. Default is "SOAP 1.1 Protocol". |
||||
* |
||||
* @param soapProtocol a string constant representing the MessageFactory protocol. |
||||
* |
||||
* @see SOAPConstants#SOAP_1_1_PROTOCOL |
||||
* @see SOAPConstants#SOAP_1_2_PROTOCOL |
||||
* @see SOAPConstants#DYNAMIC_SOAP_PROTOCOL |
||||
* @see MessageFactory#newInstance(String) |
||||
*/ |
||||
public Builder withSOAPProtocol(String soapProtocol) { |
||||
this.soapProtocol = soapProtocol; |
||||
return this; |
||||
} |
||||
|
||||
public SOAPEncoder build() { |
||||
if (jaxbContextFactory == null) { |
||||
throw new IllegalStateException("JAXBContextFactory must be non-null"); |
||||
} |
||||
return new SOAPEncoder(this); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,80 @@
@@ -0,0 +1,80 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign Authors |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
||||
* in compliance with the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License |
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
||||
* or implied. See the License for the specific language governing permissions and limitations under |
||||
* the License. |
||||
*/ |
||||
package feign.soap; |
||||
|
||||
import java.io.IOException; |
||||
import javax.xml.soap.MessageFactory; |
||||
import javax.xml.soap.SOAPConstants; |
||||
import javax.xml.soap.SOAPException; |
||||
import javax.xml.soap.SOAPFault; |
||||
import javax.xml.soap.SOAPMessage; |
||||
import javax.xml.ws.soap.SOAPFaultException; |
||||
import feign.Response; |
||||
import feign.codec.ErrorDecoder; |
||||
|
||||
/** |
||||
* Wraps the returned {@link SOAPFault} if present into a {@link SOAPFaultException}. So you need to |
||||
* catch {@link SOAPFaultException} to retrieve the reason of the {@link SOAPFault}. |
||||
* |
||||
* <p> |
||||
* If no faults is returned then the default {@link ErrorDecoder} is used to return exception and |
||||
* eventually retry the call. |
||||
* </p> |
||||
* |
||||
*/ |
||||
public class SOAPErrorDecoder implements ErrorDecoder { |
||||
|
||||
private final String soapProtocol; |
||||
|
||||
public SOAPErrorDecoder() { |
||||
this.soapProtocol = SOAPConstants.DEFAULT_SOAP_PROTOCOL; |
||||
} |
||||
|
||||
/** |
||||
* SOAPErrorDecoder constructor allowing you to specify the SOAP protocol. |
||||
* |
||||
* @param soapProtocol a string constant representing the MessageFactory protocol. |
||||
* |
||||
* @see SOAPConstants#SOAP_1_1_PROTOCOL |
||||
* @see SOAPConstants#SOAP_1_2_PROTOCOL |
||||
* @see SOAPConstants#DYNAMIC_SOAP_PROTOCOL |
||||
* @see MessageFactory#newInstance(String) |
||||
*/ |
||||
public SOAPErrorDecoder(String soapProtocol) { |
||||
this.soapProtocol = soapProtocol; |
||||
} |
||||
|
||||
@Override |
||||
public Exception decode(String methodKey, Response response) { |
||||
if (response.body() == null || response.status() == 503) |
||||
return defaultErrorDecoder(methodKey, response); |
||||
|
||||
SOAPMessage message; |
||||
try { |
||||
message = MessageFactory.newInstance(soapProtocol).createMessage(null, |
||||
response.body().asInputStream()); |
||||
if (message.getSOAPBody() != null && message.getSOAPBody().hasFault()) { |
||||
return new SOAPFaultException(message.getSOAPBody().getFault()); |
||||
} |
||||
} catch (SOAPException | IOException e) { |
||||
// ignored
|
||||
} |
||||
return defaultErrorDecoder(methodKey, response); |
||||
} |
||||
|
||||
private Exception defaultErrorDecoder(String methodKey, Response response) { |
||||
return new ErrorDecoder.Default().decode(methodKey, response); |
||||
} |
||||
|
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl |
@ -0,0 +1,395 @@
@@ -0,0 +1,395 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign Authors |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
||||
* in compliance with the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License |
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
||||
* or implied. See the License for the specific language governing permissions and limitations under |
||||
* the License. |
||||
*/ |
||||
package feign.soap; |
||||
|
||||
import static feign.Util.UTF_8; |
||||
import static feign.assertj.FeignAssertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.junit.Assert.assertEquals; |
||||
import java.lang.reflect.Type; |
||||
import java.nio.charset.Charset; |
||||
import java.util.Collection; |
||||
import java.util.Collections; |
||||
import java.util.Map; |
||||
import javax.xml.bind.annotation.XmlAccessType; |
||||
import javax.xml.bind.annotation.XmlAccessorType; |
||||
import javax.xml.bind.annotation.XmlElement; |
||||
import javax.xml.bind.annotation.XmlRootElement; |
||||
import javax.xml.bind.annotation.XmlValue; |
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.rules.ExpectedException; |
||||
import feign.Request; |
||||
import feign.Request.HttpMethod; |
||||
import feign.RequestTemplate; |
||||
import feign.Response; |
||||
import feign.Util; |
||||
import feign.codec.Encoder; |
||||
import feign.jaxb.JAXBContextFactory; |
||||
import feign.jaxb.JAXBDecoder; |
||||
|
||||
public class SOAPCodecTest { |
||||
|
||||
@Rule |
||||
public final ExpectedException thrown = ExpectedException.none(); |
||||
|
||||
@Test |
||||
public void encodesSoap() throws Exception { |
||||
Encoder encoder = new SOAPEncoder.Builder() |
||||
.withJAXBContextFactory(new JAXBContextFactory.Builder().build()) |
||||
.build(); |
||||
|
||||
GetPrice mock = new GetPrice(); |
||||
mock.item = new Item(); |
||||
mock.item.value = "Apples"; |
||||
|
||||
RequestTemplate template = new RequestTemplate(); |
||||
encoder.encode(mock, GetPrice.class, template); |
||||
|
||||
String soapEnvelop = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + |
||||
"<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" + |
||||
"<SOAP-ENV:Header/>" + |
||||
"<SOAP-ENV:Body>" + |
||||
"<GetPrice>" + |
||||
"<Item>Apples</Item>" + |
||||
"</GetPrice>" + |
||||
"</SOAP-ENV:Body>" + |
||||
"</SOAP-ENV:Envelope>"; |
||||
assertThat(template).hasBody(soapEnvelop); |
||||
} |
||||
|
||||
@Test |
||||
public void doesntEncodeParameterizedTypes() throws Exception { |
||||
thrown.expect(UnsupportedOperationException.class); |
||||
thrown.expectMessage( |
||||
"SOAP only supports encoding raw types. Found java.util.Map<java.lang.String, ?>"); |
||||
|
||||
class ParameterizedHolder { |
||||
|
||||
@SuppressWarnings("unused") |
||||
Map<String, ?> field; |
||||
} |
||||
Type parameterized = ParameterizedHolder.class.getDeclaredField("field").getGenericType(); |
||||
|
||||
RequestTemplate template = new RequestTemplate(); |
||||
new SOAPEncoder(new JAXBContextFactory.Builder().build()) |
||||
.encode(Collections.emptyMap(), parameterized, template); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void encodesSoapWithCustomJAXBMarshallerEncoding() throws Exception { |
||||
JAXBContextFactory jaxbContextFactory = |
||||
new JAXBContextFactory.Builder().withMarshallerJAXBEncoding("UTF-16").build(); |
||||
|
||||
Encoder encoder = new SOAPEncoder.Builder() |
||||
// .withWriteXmlDeclaration(true)
|
||||
.withJAXBContextFactory(jaxbContextFactory) |
||||
.withCharsetEncoding(Charset.forName("UTF-16")) |
||||
.build(); |
||||
|
||||
GetPrice mock = new GetPrice(); |
||||
mock.item = new Item(); |
||||
mock.item.value = "Apples"; |
||||
|
||||
RequestTemplate template = new RequestTemplate(); |
||||
encoder.encode(mock, GetPrice.class, template); |
||||
|
||||
String soapEnvelop = "<?xml version=\"1.0\" encoding=\"UTF-16\" ?>" + |
||||
"<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" + |
||||
"<SOAP-ENV:Header/>" + |
||||
"<SOAP-ENV:Body>" + |
||||
"<GetPrice>" + |
||||
"<Item>Apples</Item>" + |
||||
"</GetPrice>" + |
||||
"</SOAP-ENV:Body>" + |
||||
"</SOAP-ENV:Envelope>"; |
||||
byte[] utf16Bytes = soapEnvelop.getBytes("UTF-16LE"); |
||||
assertThat(template).hasBody(utf16Bytes); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void encodesSoapWithCustomJAXBSchemaLocation() throws Exception { |
||||
JAXBContextFactory jaxbContextFactory = |
||||
new JAXBContextFactory.Builder() |
||||
.withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd") |
||||
.build(); |
||||
|
||||
Encoder encoder = new SOAPEncoder(jaxbContextFactory); |
||||
|
||||
GetPrice mock = new GetPrice(); |
||||
mock.item = new Item(); |
||||
mock.item.value = "Apples"; |
||||
|
||||
RequestTemplate template = new RequestTemplate(); |
||||
encoder.encode(mock, GetPrice.class, template); |
||||
|
||||
assertThat(template).hasBody("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" |
||||
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" |
||||
+ "<SOAP-ENV:Header/>" |
||||
+ "<SOAP-ENV:Body>" |
||||
+ "<GetPrice xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://apihost http://apihost/schema.xsd\">" |
||||
+ "<Item>Apples</Item>" |
||||
+ "</GetPrice>" |
||||
+ "</SOAP-ENV:Body>" |
||||
+ "</SOAP-ENV:Envelope>"); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void encodesSoapWithCustomJAXBNoSchemaLocation() throws Exception { |
||||
JAXBContextFactory jaxbContextFactory = |
||||
new JAXBContextFactory.Builder() |
||||
.withMarshallerNoNamespaceSchemaLocation("http://apihost/schema.xsd") |
||||
.build(); |
||||
|
||||
Encoder encoder = new SOAPEncoder(jaxbContextFactory); |
||||
|
||||
GetPrice mock = new GetPrice(); |
||||
mock.item = new Item(); |
||||
mock.item.value = "Apples"; |
||||
|
||||
RequestTemplate template = new RequestTemplate(); |
||||
encoder.encode(mock, GetPrice.class, template); |
||||
|
||||
assertThat(template).hasBody("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" |
||||
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" |
||||
+ "<SOAP-ENV:Header/>" |
||||
+ "<SOAP-ENV:Body>" |
||||
+ "<GetPrice xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://apihost/schema.xsd\">" |
||||
+ "<Item>Apples</Item>" |
||||
+ "</GetPrice>" |
||||
+ "</SOAP-ENV:Body>" |
||||
+ "</SOAP-ENV:Envelope>"); |
||||
} |
||||
|
||||
@Test |
||||
public void encodesSoapWithCustomJAXBFormattedOuput() throws Exception { |
||||
Encoder encoder = new SOAPEncoder.Builder().withFormattedOutput(true) |
||||
.withJAXBContextFactory(new JAXBContextFactory.Builder() |
||||
.build()) |
||||
.build(); |
||||
|
||||
GetPrice mock = new GetPrice(); |
||||
mock.item = new Item(); |
||||
mock.item.value = "Apples"; |
||||
|
||||
RequestTemplate template = new RequestTemplate(); |
||||
encoder.encode(mock, GetPrice.class, template); |
||||
|
||||
assertThat(template).hasBody("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + |
||||
"<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" + |
||||
" <SOAP-ENV:Header/>\n" + |
||||
" <SOAP-ENV:Body>\n" + |
||||
" <GetPrice>\n" + |
||||
" <Item>Apples</Item>\n" + |
||||
" </GetPrice>\n" + |
||||
" </SOAP-ENV:Body>\n" + |
||||
"</SOAP-ENV:Envelope>\n" + |
||||
""); |
||||
} |
||||
|
||||
@Test |
||||
public void decodesSoap() throws Exception { |
||||
GetPrice mock = new GetPrice(); |
||||
mock.item = new Item(); |
||||
mock.item.value = "Apples"; |
||||
|
||||
String mockSoapEnvelop = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" |
||||
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" |
||||
+ "<SOAP-ENV:Header/>" |
||||
+ "<SOAP-ENV:Body>" |
||||
+ "<GetPrice xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://apihost/schema.xsd\">" |
||||
+ "<Item>Apples</Item>" |
||||
+ "</GetPrice>" |
||||
+ "</SOAP-ENV:Body>" |
||||
+ "</SOAP-ENV:Envelope>"; |
||||
|
||||
Response response = Response.builder() |
||||
.status(200) |
||||
.reason("OK") |
||||
.request(Request.create(HttpMethod.GET, "/api", Collections.emptyMap(), null, Util.UTF_8)) |
||||
.headers(Collections.emptyMap()) |
||||
.body(mockSoapEnvelop, UTF_8) |
||||
.build(); |
||||
|
||||
SOAPDecoder decoder = new SOAPDecoder(new JAXBContextFactory.Builder().build()); |
||||
|
||||
assertEquals(mock, decoder.decode(response, GetPrice.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void decodesSoap1_2Protocol() throws Exception { |
||||
GetPrice mock = new GetPrice(); |
||||
mock.item = new Item(); |
||||
mock.item.value = "Apples"; |
||||
|
||||
String mockSoapEnvelop = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" |
||||
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" |
||||
+ "<SOAP-ENV:Header/>" |
||||
+ "<SOAP-ENV:Body>" |
||||
+ "<GetPrice xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://apihost/schema.xsd\">" |
||||
+ "<Item>Apples</Item>" |
||||
+ "</GetPrice>" |
||||
+ "</SOAP-ENV:Body>" |
||||
+ "</SOAP-ENV:Envelope>"; |
||||
|
||||
Response response = Response.builder() |
||||
.status(200) |
||||
.reason("OK") |
||||
.request(Request.create(HttpMethod.GET, "/api", Collections.emptyMap(), null, Util.UTF_8)) |
||||
.headers(Collections.emptyMap()) |
||||
.body(mockSoapEnvelop, UTF_8) |
||||
.build(); |
||||
|
||||
SOAPDecoder decoder = new SOAPDecoder(new JAXBContextFactory.Builder().build()); |
||||
|
||||
assertEquals(mock, decoder.decode(response, GetPrice.class)); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void doesntDecodeParameterizedTypes() throws Exception { |
||||
thrown.expect(feign.codec.DecodeException.class); |
||||
thrown.expectMessage( |
||||
"java.util.Map is an interface, and JAXB can't handle interfaces.\n" |
||||
+ "\tthis problem is related to the following location:\n" |
||||
+ "\t\tat java.util.Map"); |
||||
|
||||
class ParameterizedHolder { |
||||
|
||||
@SuppressWarnings("unused") |
||||
Map<String, ?> field; |
||||
} |
||||
Type parameterized = ParameterizedHolder.class.getDeclaredField("field").getGenericType(); |
||||
|
||||
Response response = Response.builder() |
||||
.status(200) |
||||
.reason("OK") |
||||
.request(Request.create(HttpMethod.GET, "/api", Collections.emptyMap(), null, Util.UTF_8)) |
||||
.headers(Collections.<String, Collection<String>>emptyMap()) |
||||
.body("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" |
||||
+ "<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\">" |
||||
+ "<Header/>" |
||||
+ "<Body>" |
||||
+ "<GetPrice>" |
||||
+ "<Item>Apples</Item>" |
||||
+ "</GetPrice>" |
||||
+ "</Body>" |
||||
+ "</Envelope>", UTF_8) |
||||
.build(); |
||||
|
||||
new SOAPDecoder(new JAXBContextFactory.Builder().build()).decode(response, parameterized); |
||||
} |
||||
|
||||
@XmlRootElement |
||||
static class Box<T> { |
||||
|
||||
@XmlElement |
||||
private T t; |
||||
|
||||
public void set(T t) { |
||||
this.t = t; |
||||
} |
||||
|
||||
} |
||||
|
||||
@Test |
||||
public void decodeAnnotatedParameterizedTypes() throws Exception { |
||||
JAXBContextFactory jaxbContextFactory = |
||||
new JAXBContextFactory.Builder().withMarshallerFormattedOutput(true).build(); |
||||
|
||||
Encoder encoder = new SOAPEncoder(jaxbContextFactory); |
||||
|
||||
Box<String> boxStr = new Box<>(); |
||||
boxStr.set("hello"); |
||||
Box<Box<String>> boxBoxStr = new Box<>(); |
||||
boxBoxStr.set(boxStr); |
||||
RequestTemplate template = new RequestTemplate(); |
||||
encoder.encode(boxBoxStr, Box.class, template); |
||||
|
||||
Response response = Response.builder() |
||||
.status(200) |
||||
.reason("OK") |
||||
.request(Request.create(HttpMethod.GET, "/api", Collections.emptyMap(), null, Util.UTF_8)) |
||||
.headers(Collections.<String, Collection<String>>emptyMap()) |
||||
.body(template.body()) |
||||
.build(); |
||||
|
||||
new SOAPDecoder(new JAXBContextFactory.Builder().build()).decode(response, Box.class); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Enabled via {@link feign.Feign.Builder#decode404()} |
||||
*/ |
||||
@Test |
||||
public void notFoundDecodesToEmpty() throws Exception { |
||||
Response response = Response.builder() |
||||
.status(404) |
||||
.reason("NOT FOUND") |
||||
.request(Request.create(HttpMethod.GET, "/api", Collections.emptyMap(), null, Util.UTF_8)) |
||||
.headers(Collections.<String, Collection<String>>emptyMap()) |
||||
.build(); |
||||
assertThat((byte[]) new JAXBDecoder(new JAXBContextFactory.Builder().build()) |
||||
.decode(response, byte[].class)).isEmpty(); |
||||
} |
||||
|
||||
|
||||
@XmlRootElement(name = "GetPrice") |
||||
@XmlAccessorType(XmlAccessType.FIELD) |
||||
static class GetPrice { |
||||
|
||||
@XmlElement(name = "Item") |
||||
private Item item; |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (obj instanceof GetPrice) { |
||||
GetPrice getPrice = (GetPrice) obj; |
||||
return item.value.equals(getPrice.item.value); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return item.value != null ? item.value.hashCode() : 0; |
||||
} |
||||
} |
||||
|
||||
@XmlRootElement(name = "Item") |
||||
@XmlAccessorType(XmlAccessType.FIELD) |
||||
static class Item { |
||||
|
||||
@XmlValue |
||||
private String value; |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (obj instanceof Item) { |
||||
Item item = (Item) obj; |
||||
return value.equals(item.value); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return value != null ? value.hashCode() : 0; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,122 @@
@@ -0,0 +1,122 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign Authors |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
||||
* in compliance with the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License |
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
||||
* or implied. See the License for the specific language governing permissions and limitations under |
||||
* the License. |
||||
*/ |
||||
package feign.soap; |
||||
|
||||
import static feign.Util.UTF_8; |
||||
import java.io.DataInputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.util.Collections; |
||||
import javax.xml.soap.SOAPConstants; |
||||
import javax.xml.ws.soap.SOAPFaultException; |
||||
import org.assertj.core.api.Assertions; |
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.rules.ExpectedException; |
||||
import feign.FeignException; |
||||
import feign.Request; |
||||
import feign.Request.HttpMethod; |
||||
import feign.Response; |
||||
import feign.Util; |
||||
import feign.jaxb.JAXBContextFactory; |
||||
|
||||
public class SOAPFaultDecoderTest { |
||||
|
||||
@Rule |
||||
public final ExpectedException thrown = ExpectedException.none(); |
||||
|
||||
@Test |
||||
public void soapDecoderThrowsSOAPFaultException() throws IOException { |
||||
|
||||
thrown.expect(SOAPFaultException.class); |
||||
thrown.expectMessage("Processing error"); |
||||
|
||||
Response response = Response.builder() |
||||
.status(200) |
||||
.reason("OK") |
||||
.request(Request.create(HttpMethod.GET, "/api", Collections.emptyMap(), null, Util.UTF_8)) |
||||
.headers(Collections.emptyMap()) |
||||
.body(getResourceBytes("/samples/SOAP_1_2_FAULT.xml")) |
||||
.build(); |
||||
|
||||
new SOAPDecoder.Builder().withSOAPProtocol(SOAPConstants.SOAP_1_2_PROTOCOL) |
||||
.withJAXBContextFactory(new JAXBContextFactory.Builder().build()).build() |
||||
.decode(response, Object.class); |
||||
} |
||||
|
||||
@Test |
||||
public void errorDecoderReturnsSOAPFaultException() throws IOException { |
||||
Response response = Response.builder() |
||||
.status(400) |
||||
.reason("BAD REQUEST") |
||||
.request(Request.create(HttpMethod.GET, "/api", Collections.emptyMap(), null, Util.UTF_8)) |
||||
.headers(Collections.emptyMap()) |
||||
.body(getResourceBytes("/samples/SOAP_1_1_FAULT.xml")) |
||||
.build(); |
||||
|
||||
Exception error = |
||||
new SOAPErrorDecoder().decode("Service#foo()", response); |
||||
Assertions.assertThat(error).isInstanceOf(SOAPFaultException.class) |
||||
.hasMessage("Message was not SOAP 1.1 compliant"); |
||||
} |
||||
|
||||
@Test |
||||
public void errorDecoderReturnsFeignExceptionOn503Status() throws IOException { |
||||
Response response = Response.builder() |
||||
.status(503) |
||||
.reason("Service Unavailable") |
||||
.request(Request.create(HttpMethod.GET, "/api", Collections.emptyMap(), null, Util.UTF_8)) |
||||
.headers(Collections.emptyMap()) |
||||
.body("Service Unavailable", UTF_8) |
||||
.build(); |
||||
|
||||
Exception error = |
||||
new SOAPErrorDecoder().decode("Service#foo()", response); |
||||
|
||||
Assertions.assertThat(error).isInstanceOf(FeignException.class) |
||||
.hasMessage("status 503 reading Service#foo()"); |
||||
} |
||||
|
||||
@Test |
||||
public void errorDecoderReturnsFeignExceptionOnEmptyFault() throws IOException { |
||||
Response response = Response.builder() |
||||
.status(500) |
||||
.reason("Internal Server Error") |
||||
.request(Request.create(HttpMethod.GET, "/api", Collections.emptyMap(), null, Util.UTF_8)) |
||||
.headers(Collections.emptyMap()) |
||||
.body("<?xml version = '1.0' encoding = 'UTF-8'?>\n" + |
||||
"<SOAP-ENV:Envelope\n" + |
||||
" xmlns:SOAP-ENV = \"http://schemas.xmlsoap.org/soap/envelope/\"\n" + |
||||
" xmlns:xsi = \"http://www.w3.org/1999/XMLSchema-instance\"\n" + |
||||
" xmlns:xsd = \"http://www.w3.org/1999/XMLSchema\">\n" + |
||||
" <SOAP-ENV:Body>\n" + |
||||
" </SOAP-ENV:Body>\n" + |
||||
"</SOAP-ENV:Envelope>", UTF_8) |
||||
.build(); |
||||
|
||||
Exception error = |
||||
new SOAPErrorDecoder().decode("Service#foo()", response); |
||||
|
||||
Assertions.assertThat(error).isInstanceOf(FeignException.class) |
||||
.hasMessage("status 500 reading Service#foo()"); |
||||
} |
||||
|
||||
private static byte[] getResourceBytes(String resourcePath) throws IOException { |
||||
InputStream resourceAsStream = SOAPFaultDecoderTest.class.getResourceAsStream(resourcePath); |
||||
byte[] bytes = new byte[resourceAsStream.available()]; |
||||
new DataInputStream(resourceAsStream).readFully(bytes); |
||||
return bytes; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
<?xml version = '1.0' encoding = 'UTF-8'?> |
||||
<SOAP-ENV:Envelope |
||||
xmlns:SOAP-ENV = "http://schemas.xmlsoap.org/soap/envelope/" |
||||
xmlns:xsi = "http://www.w3.org/1999/XMLSchema-instance" |
||||
xmlns:xsd = "http://www.w3.org/1999/XMLSchema"> |
||||
|
||||
<SOAP-ENV:Body> |
||||
<SOAP-ENV:Fault> |
||||
<faultcode xsi:type = "xsd:string">SOAP-ENV:Client</faultcode> |
||||
<faultstring xsi:type = "xsd:string">Message was not SOAP 1.1 compliant</faultstring> |
||||
</SOAP-ENV:Fault> |
||||
</SOAP-ENV:Body> |
||||
</SOAP-ENV:Envelope> |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?> |
||||
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> |
||||
<env:Body> |
||||
<env:Fault> |
||||
<env:Code> |
||||
<env:Value>env:Sender</env:Value> |
||||
<env:Subcode> |
||||
<env:Value>rpc:BadArguments</env:Value> |
||||
</env:Subcode> |
||||
</env:Code> |
||||
<env:Reason> |
||||
<env:Text xml:lang="en-US">Processing error</env:Text> |
||||
</env:Reason> |
||||
<env:Detail> |
||||
<e:myFaultDetails |
||||
xmlns:e="http://travelcompany.example.org/faults"> |
||||
<e:message>Name does not match card number</e:message> |
||||
<e:errorcode>999</e:errorcode> |
||||
</e:myFaultDetails> |
||||
</env:Detail> |
||||
</env:Fault> |
||||
</env:Body> |
||||
</env:Envelope> |
Loading…
Reference in new issue