Skip to content

Commit 6294155

Browse files
Pierre Lakrebpilak
authored andcommitted
Adding SOAP CoDec (+ JAXB modifications)
1 parent b40498f commit 6294155

File tree

7 files changed

+414
-32
lines changed

7 files changed

+414
-32
lines changed

jaxb/src/main/java/feign/jaxb/JAXBContextFactory.java

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
package feign.jaxb;
1515

1616
import java.util.HashMap;
17-
import java.util.Iterator;
1817
import java.util.Map;
18+
import java.util.Map.Entry;
1919
import java.util.concurrent.ConcurrentHashMap;
2020
import javax.xml.bind.JAXBContext;
2121
import javax.xml.bind.JAXBException;
@@ -29,8 +29,8 @@
2929
*/
3030
public final class JAXBContextFactory {
3131

32-
private final ConcurrentHashMap<Class, JAXBContext> jaxbContexts =
33-
new ConcurrentHashMap<Class, JAXBContext>(64);
32+
private final ConcurrentHashMap<Class<?>, JAXBContext> jaxbContexts =
33+
new ConcurrentHashMap<>(64);
3434
private final Map<String, Object> properties;
3535

3636
private JAXBContextFactory(Map<String, Object> properties) {
@@ -41,26 +41,21 @@ private JAXBContextFactory(Map<String, Object> properties) {
4141
* Creates a new {@link javax.xml.bind.Unmarshaller} that handles the supplied class.
4242
*/
4343
public Unmarshaller createUnmarshaller(Class<?> clazz) throws JAXBException {
44-
JAXBContext ctx = getContext(clazz);
45-
return ctx.createUnmarshaller();
44+
return getContext(clazz).createUnmarshaller();
4645
}
4746

4847
/**
4948
* Creates a new {@link javax.xml.bind.Marshaller} that handles the supplied class.
5049
*/
5150
public Marshaller createMarshaller(Class<?> clazz) throws JAXBException {
52-
JAXBContext ctx = getContext(clazz);
53-
Marshaller marshaller = ctx.createMarshaller();
51+
Marshaller marshaller = getContext(clazz).createMarshaller();
5452
setMarshallerProperties(marshaller);
5553
return marshaller;
5654
}
5755

5856
private void setMarshallerProperties(Marshaller marshaller) throws PropertyException {
59-
Iterator<String> keys = properties.keySet().iterator();
60-
61-
while (keys.hasNext()) {
62-
String key = keys.next();
63-
marshaller.setProperty(key, properties.get(key));
57+
for (Entry<String, Object> en : properties.entrySet()) {
58+
marshaller.setProperty(en.getKey(), en.getValue());
6459
}
6560
}
6661

@@ -74,11 +69,11 @@ private JAXBContext getContext(Class<?> clazz) throws JAXBException {
7469
}
7570

7671
/**
77-
* Creates instances of {@link feign.jaxb.JAXBContextFactory}
72+
* Creates instances of {@link feign.jaxb.JAXBContextFactory}.
7873
*/
7974
public static class Builder {
8075

81-
private final Map<String, Object> properties = new HashMap<String, Object>(5);
76+
private final Map<String, Object> properties = new HashMap<>(10);
8277

8378
/**
8479
* Sets the jaxb.encoding property of any Marshaller created by this factory.
@@ -120,6 +115,24 @@ public Builder withMarshallerFragment(Boolean value) {
120115
return this;
121116
}
122117

118+
/**
119+
* Sets the given property of any Marshaller created by this factory.
120+
*
121+
* <p>
122+
* Example : <br>
123+
* <br>
124+
* <code>
125+
* new JAXBContextFactory.Builder()
126+
* .withProperty("com.sun.xml.internal.bind.xmlHeaders", "&lt;!DOCTYPE Example SYSTEM \&quot;example.dtd\&quot;&gt;")
127+
* .build();
128+
* </code>
129+
* </p>
130+
*/
131+
public Builder withProperty(String key, Object value) {
132+
properties.put(key, value);
133+
return this;
134+
}
135+
123136
/**
124137
* Creates a new {@link feign.jaxb.JAXBContextFactory} instance.
125138
*/

jaxb/src/main/java/feign/jaxb/JAXBDecoder.java

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@
1414
package feign.jaxb;
1515

1616
import java.io.IOException;
17-
import java.lang.reflect.Type;
1817
import java.lang.reflect.ParameterizedType;
18+
import java.lang.reflect.Type;
1919
import javax.xml.bind.JAXBException;
20-
import javax.xml.bind.Unmarshaller;
2120
import javax.xml.parsers.ParserConfigurationException;
2221
import javax.xml.parsers.SAXParserFactory;
23-
import javax.xml.transform.Source;
2422
import javax.xml.transform.sax.SAXSource;
2523
import feign.Response;
2624
import feign.Util;
@@ -51,15 +49,15 @@
5149
*/
5250
public class JAXBDecoder implements Decoder {
5351

54-
private final JAXBContextFactory jaxbContextFactory;
52+
final JAXBContextFactory jaxbContextFactory;
5553
private final boolean namespaceAware;
5654

5755
public JAXBDecoder(JAXBContextFactory jaxbContextFactory) {
5856
this.jaxbContextFactory = jaxbContextFactory;
5957
this.namespaceAware = true;
6058
}
6159

62-
private JAXBDecoder(Builder builder) {
60+
JAXBDecoder(Builder builder) {
6361
this.jaxbContextFactory = builder.jaxbContextFactory;
6462
this.namespaceAware = builder.namespaceAware;
6563
}
@@ -90,15 +88,10 @@ public Object decode(Response response, Type type) throws IOException {
9088
false);
9189
saxParserFactory.setNamespaceAware(namespaceAware);
9290

93-
Source source = new SAXSource(saxParserFactory.newSAXParser().getXMLReader(),
94-
new InputSource(response.body().asInputStream()));
95-
Unmarshaller unmarshaller = jaxbContextFactory.createUnmarshaller((Class) type);
96-
return unmarshaller.unmarshal(source);
97-
} catch (JAXBException e) {
98-
throw new DecodeException(e.toString(), e);
99-
} catch (ParserConfigurationException e) {
100-
throw new DecodeException(e.toString(), e);
101-
} catch (SAXException e) {
91+
return jaxbContextFactory.createUnmarshaller((Class<?>) type).unmarshal(new SAXSource(
92+
saxParserFactory.newSAXParser().getXMLReader(),
93+
new InputSource(response.body().asInputStream())));
94+
} catch (JAXBException | ParserConfigurationException | SAXException e) {
10295
throw new DecodeException(e.toString(), e);
10396
} finally {
10497
if (response.body() != null) {
@@ -108,8 +101,8 @@ public Object decode(Response response, Type type) throws IOException {
108101
}
109102

110103
public static class Builder {
111-
private boolean namespaceAware = true;
112-
private JAXBContextFactory jaxbContextFactory;
104+
boolean namespaceAware = true;
105+
JAXBContextFactory jaxbContextFactory;
113106

114107
/**
115108
* Controls whether the underlying XML parser is namespace aware. Default is true.

jaxb/src/main/java/feign/jaxb/JAXBEncoder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
*/
4444
public class JAXBEncoder implements Encoder {
4545

46-
private final JAXBContextFactory jaxbContextFactory;
46+
final JAXBContextFactory jaxbContextFactory;
4747

4848
public JAXBEncoder(JAXBContextFactory jaxbContextFactory) {
4949
this.jaxbContextFactory = jaxbContextFactory;
@@ -56,7 +56,7 @@ public void encode(Object object, Type bodyType, RequestTemplate template) {
5656
"JAXB only supports encoding raw types. Found " + bodyType);
5757
}
5858
try {
59-
Marshaller marshaller = jaxbContextFactory.createMarshaller((Class) bodyType);
59+
Marshaller marshaller = jaxbContextFactory.createMarshaller((Class<?>) bodyType);
6060
StringWriter stringWriter = new StringWriter();
6161
marshaller.marshal(object, stringWriter);
6262
template.body(stringWriter.toString());
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* Copyright 2012-2018 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign.jaxb;
15+
16+
import java.io.IOException;
17+
import java.lang.reflect.ParameterizedType;
18+
import java.lang.reflect.Type;
19+
import javax.xml.bind.JAXBException;
20+
import javax.xml.soap.MessageFactory;
21+
import javax.xml.soap.SOAPException;
22+
import javax.xml.soap.SOAPMessage;
23+
import feign.Response;
24+
import feign.Util;
25+
import feign.codec.DecodeException;
26+
27+
public class SOAPDecoder extends JAXBDecoder {
28+
29+
public SOAPDecoder(JAXBContextFactory jaxbContextFactory) {
30+
super(jaxbContextFactory);
31+
}
32+
33+
private SOAPDecoder(Builder builder) {
34+
super(builder);
35+
}
36+
37+
@Override
38+
public Object decode(Response response, Type type) throws IOException {
39+
if (response.status() == 404)
40+
return Util.emptyValueOf(type);
41+
if (response.body() == null)
42+
return null;
43+
while (type instanceof ParameterizedType) {
44+
ParameterizedType ptype = (ParameterizedType) type;
45+
type = ptype.getRawType();
46+
}
47+
if (!(type instanceof Class)) {
48+
throw new UnsupportedOperationException(
49+
"SOAP only supports decoding raw types. Found " + type);
50+
}
51+
52+
try {
53+
SOAPMessage message =
54+
MessageFactory.newInstance().createMessage(null, response.body().asInputStream());
55+
return jaxbContextFactory.createUnmarshaller((Class<?>) type)
56+
.unmarshal(message.getSOAPBody().extractContentAsDocument());
57+
} catch (JAXBException | SOAPException e) {
58+
throw new DecodeException(e.toString(), e);
59+
} finally {
60+
if (response.body() != null) {
61+
response.body().close();
62+
}
63+
}
64+
65+
}
66+
67+
public static class Builder extends JAXBDecoder.Builder {
68+
@Override
69+
public SOAPDecoder build() {
70+
if (jaxbContextFactory == null) {
71+
throw new IllegalStateException("JAXBContextFactory must be non-null");
72+
}
73+
return new SOAPDecoder(this);
74+
}
75+
}
76+
77+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/**
2+
* Copyright 2012-2018 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign.jaxb;
15+
16+
import feign.RequestTemplate;
17+
import feign.codec.EncodeException;
18+
import org.w3c.dom.Document;
19+
20+
import javax.xml.bind.JAXBException;
21+
import javax.xml.bind.Marshaller;
22+
import javax.xml.parsers.DocumentBuilderFactory;
23+
import javax.xml.parsers.ParserConfigurationException;
24+
import javax.xml.soap.MessageFactory;
25+
import javax.xml.soap.SOAPException;
26+
import javax.xml.soap.SOAPMessage;
27+
import java.io.ByteArrayOutputStream;
28+
import java.io.IOException;
29+
import java.lang.reflect.Type;
30+
import java.nio.charset.Charset;
31+
32+
public class SOAPEncoder extends JAXBEncoder {
33+
34+
private final boolean writeXmlDeclaration;
35+
private final Charset charsetEncoding;
36+
37+
private SOAPEncoder(Builder builder) {
38+
this(builder.jaxbContextFactory, builder.writeXmlDeclaration, builder.charsetEncoding);
39+
}
40+
41+
public SOAPEncoder(JAXBContextFactory jaxbContextFactory) {
42+
this(jaxbContextFactory, true, Charset.defaultCharset());
43+
}
44+
45+
SOAPEncoder(JAXBContextFactory jaxbContextFactory,
46+
boolean writeXmlDeclaration, Charset charsetEncoding) {
47+
super(jaxbContextFactory);
48+
this.writeXmlDeclaration = writeXmlDeclaration;
49+
this.charsetEncoding = charsetEncoding;
50+
}
51+
52+
@Override
53+
public void encode(Object object, Type bodyType, RequestTemplate template) {
54+
if (!(bodyType instanceof Class)) {
55+
throw new UnsupportedOperationException(
56+
"SOAP only supports encoding raw types. Found " + bodyType);
57+
}
58+
try {
59+
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
60+
Marshaller marshaller = jaxbContextFactory.createMarshaller((Class<?>) bodyType);
61+
marshaller.marshal(object, document);
62+
SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
63+
soapMessage.setProperty(SOAPMessage.WRITE_XML_DECLARATION,
64+
Boolean.toString(writeXmlDeclaration));
65+
soapMessage.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, charsetEncoding.displayName());
66+
soapMessage.getSOAPBody().addDocument(document);
67+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
68+
soapMessage.writeTo(bos);
69+
template.body(new String(bos.toByteArray()));
70+
} catch (SOAPException | JAXBException | ParserConfigurationException | IOException e) {
71+
throw new EncodeException(e.toString(), e);
72+
}
73+
}
74+
75+
/**
76+
* Creates instances of {@link SOAPEncoder}.
77+
*/
78+
public static class Builder {
79+
80+
private JAXBContextFactory jaxbContextFactory;
81+
private boolean writeXmlDeclaration = true;
82+
private Charset charsetEncoding = Charset.defaultCharset();
83+
84+
public Builder withCharsetEncoding(Charset charsetEncoding) {
85+
this.charsetEncoding = charsetEncoding;
86+
return this;
87+
}
88+
89+
public Builder withWriteXmlDeclaration(boolean writeXmlDeclaration) {
90+
this.writeXmlDeclaration = writeXmlDeclaration;
91+
return this;
92+
}
93+
94+
public Builder withJAXBContextFactory(JAXBContextFactory jaxbContextFactory) {
95+
this.jaxbContextFactory = jaxbContextFactory;
96+
return this;
97+
}
98+
99+
public SOAPEncoder build() {
100+
if (jaxbContextFactory == null) {
101+
throw new IllegalStateException("JAXBContextFactory must be non-null");
102+
}
103+
return new SOAPEncoder(this);
104+
}
105+
}
106+
}

0 commit comments

Comments
 (0)