Skip to content

Commit

Permalink
Adding SOAP CoDec (+ JAXB modifications)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pierre Lakreb authored and pilak committed Nov 1, 2018
1 parent e772985 commit 2ceedb3
Show file tree
Hide file tree
Showing 13 changed files with 1,195 additions and 27 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,28 @@ public class Example {
}
```

### SOAP
[SOAP](./soap) includes an encoder and decoder you can use with an XML API.


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 class Example {
public static void main(String[] args) {
Api api = Feign.builder()
.encoder(new SOAPEncoder(jaxbFactory))
.decoder(new SOAPDecoder(jaxbFactory))
.errorDecoder(new SOAPErrorDecoder())
.target(MyApi.class, "http://api");
}
}
```

NB: you may also need to add `SOAPErrorDecoder` if SOAP Faults are returned in response with error http codes (4xx, 5xx, ...)

### SLF4J
[SLF4JModule](./slf4j) allows directing Feign's logging to [SLF4J](http://www.slf4j.org/), allowing you to easily use a logging backend of your choice (Logback, Log4J, etc.)

Expand Down
41 changes: 27 additions & 14 deletions jaxb/src/main/java/feign/jaxb/JAXBContextFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
package feign.jaxb;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
Expand All @@ -31,8 +31,8 @@
*/
public final class JAXBContextFactory {

private final ConcurrentHashMap<Class, JAXBContext> jaxbContexts =
new ConcurrentHashMap<Class, JAXBContext>(64);
private final ConcurrentHashMap<Class<?>, JAXBContext> jaxbContexts =
new ConcurrentHashMap<>(64);
private final Map<String, Object> properties;

private JAXBContextFactory(Map<String, Object> properties) {
Expand All @@ -43,26 +43,21 @@ private JAXBContextFactory(Map<String, Object> properties) {
* Creates a new {@link javax.xml.bind.Unmarshaller} that handles the supplied class.
*/
public Unmarshaller createUnmarshaller(Class<?> clazz) throws JAXBException {
JAXBContext ctx = getContext(clazz);
return ctx.createUnmarshaller();
return getContext(clazz).createUnmarshaller();
}

/**
* Creates a new {@link javax.xml.bind.Marshaller} that handles the supplied class.
*/
public Marshaller createMarshaller(Class<?> clazz) throws JAXBException {
JAXBContext ctx = getContext(clazz);
Marshaller marshaller = ctx.createMarshaller();
Marshaller marshaller = getContext(clazz).createMarshaller();
setMarshallerProperties(marshaller);
return marshaller;
}

private void setMarshallerProperties(Marshaller marshaller) throws PropertyException {
Iterator<String> keys = properties.keySet().iterator();

while (keys.hasNext()) {
String key = keys.next();
marshaller.setProperty(key, properties.get(key));
for (Entry<String, Object> en : properties.entrySet()) {
marshaller.setProperty(en.getKey(), en.getValue());
}
}

Expand Down Expand Up @@ -90,11 +85,11 @@ private void preloadContextCache(List<Class<?>> classes) throws JAXBException {
}

/**
* Creates instances of {@link feign.jaxb.JAXBContextFactory}
* Creates instances of {@link feign.jaxb.JAXBContextFactory}.
*/
public static class Builder {

private final Map<String, Object> properties = new HashMap<String, Object>(5);
private final Map<String, Object> properties = new HashMap<>(10);

/**
* Sets the jaxb.encoding property of any Marshaller created by this factory.
Expand Down Expand Up @@ -136,6 +131,24 @@ public Builder withMarshallerFragment(Boolean value) {
return this;
}

/**
* Sets the given property of any Marshaller created by this factory.
*
* <p>
* Example : <br>
* <br>
* <code>
* new JAXBContextFactory.Builder()
* .withProperty("com.sun.xml.internal.bind.xmlHeaders", "&lt;!DOCTYPE Example SYSTEM \&quot;example.dtd\&quot;&gt;")
* .build();
* </code>
* </p>
*/
public Builder withProperty(String key, Object value) {
properties.put(key, value);
return this;
}

/**
* Creates a new {@link feign.jaxb.JAXBContextFactory} instance with a lazy loading cached
* context
Expand Down
17 changes: 5 additions & 12 deletions jaxb/src/main/java/feign/jaxb/JAXBDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@
package feign.jaxb;

import java.io.IOException;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import feign.Response;
import feign.Util;
Expand Down Expand Up @@ -90,15 +88,10 @@ public Object decode(Response response, Type type) throws IOException {
false);
saxParserFactory.setNamespaceAware(namespaceAware);

Source source = new SAXSource(saxParserFactory.newSAXParser().getXMLReader(),
new InputSource(response.body().asInputStream()));
Unmarshaller unmarshaller = jaxbContextFactory.createUnmarshaller((Class) type);
return unmarshaller.unmarshal(source);
} catch (JAXBException e) {
throw new DecodeException(e.toString(), e);
} catch (ParserConfigurationException e) {
throw new DecodeException(e.toString(), e);
} catch (SAXException e) {
return jaxbContextFactory.createUnmarshaller((Class<?>) type).unmarshal(new SAXSource(
saxParserFactory.newSAXParser().getXMLReader(),
new InputSource(response.body().asInputStream())));
} catch (JAXBException | ParserConfigurationException | SAXException e) {
throw new DecodeException(e.toString(), e);
} finally {
if (response.body() != null) {
Expand Down
2 changes: 1 addition & 1 deletion jaxb/src/main/java/feign/jaxb/JAXBEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void encode(Object object, Type bodyType, RequestTemplate template) {
"JAXB only supports encoding raw types. Found " + bodyType);
}
try {
Marshaller marshaller = jaxbContextFactory.createMarshaller((Class) bodyType);
Marshaller marshaller = jaxbContextFactory.createMarshaller((Class<?>) bodyType);
StringWriter stringWriter = new StringWriter();
marshaller.marshal(object, stringWriter);
template.body(stringWriter.toString());
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<module>ribbon</module>
<module>sax</module>
<module>slf4j</module>
<module>soap</module>
<module>reactive</module>
<module>example-github</module>
<module>example-wikipedia</module>
Expand All @@ -52,6 +53,9 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.resourceEncoding>UTF-8</project.build.resourceEncoding>

<!-- specifying jvm arguments -->
<jvm.options>-Duser.language=en</jvm.options>

<!-- default bytecode version for src/main -->
<main.java.version>1.8</main.java.version>
<main.signature.artifact>java18</main.signature.artifact>
Expand Down Expand Up @@ -317,6 +321,7 @@
<configuration>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
<trimStackTrace>false</trimStackTrace>
<argLine>${jvm.options}</argLine>
</configuration>
<dependencies>
<dependency>
Expand Down
50 changes: 50 additions & 0 deletions soap/README.md
Original file line number Diff line number Diff line change
@@ -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:

```
api = Feign.builder()
.encoder(new SOAPEncoder(jaxbFactory))
.decoder(new SOAPDecoder(jaxbFactory))
.errorDecoder(new SOAPErrorDecoder())
.target(MyApi.class, "http://api");
```
89 changes: 89 additions & 0 deletions soap/pom.xml
Original file line number Diff line number Diff line change
@@ -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>
Loading

0 comments on commit 2ceedb3

Please sign in to comment.