-
Notifications
You must be signed in to change notification settings - Fork 0
/
JAXBDecoder.java
134 lines (121 loc) · 4.56 KB
/
JAXBDecoder.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/**
* 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.jaxb;
import java.io.IOException;
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;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Decodes responses using JAXB. <br>
* <p>
* Basic example with with Feign.Builder:
* </p>
*
* <pre>
* JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder()
* .withMarshallerJAXBEncoding("UTF-8")
* .withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd")
* .build();
*
* api = Feign.builder()
* .decoder(new JAXBDecoder(jaxbFactory))
* .target(MyApi.class, "http://api");
* </pre>
* <p>
* The JAXBContextFactory should be reused across requests as it caches the created JAXB contexts.
* </p>
*/
public class JAXBDecoder implements Decoder {
private final JAXBContextFactory jaxbContextFactory;
private final boolean namespaceAware;
public JAXBDecoder(JAXBContextFactory jaxbContextFactory) {
this.jaxbContextFactory = jaxbContextFactory;
this.namespaceAware = true;
}
private JAXBDecoder(Builder builder) {
this.jaxbContextFactory = builder.jaxbContextFactory;
this.namespaceAware = builder.namespaceAware;
}
@Override
public Object decode(Response response, Type type) throws IOException {
if (response.status() == 404)
return Util.emptyValueOf(type);
if (response.body() == null)
return null;
if (type instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) type;
type = ptype.getRawType();
}
if (!(type instanceof Class)) {
throw new UnsupportedOperationException(
"JAXB only supports decoding raw types. Found " + type);
}
try {
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
/* Explicitly control sax configuration to prevent XXE attacks */
saxParserFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
saxParserFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
saxParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false);
saxParserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",
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) {
throw new DecodeException(e.toString(), e);
} finally {
if (response.body() != null) {
response.body().close();
}
}
}
public static class Builder {
private boolean namespaceAware = true;
private JAXBContextFactory jaxbContextFactory;
/**
* Controls whether the underlying XML parser is namespace aware. Default is true.
*/
public Builder withNamespaceAware(boolean namespaceAware) {
this.namespaceAware = namespaceAware;
return this;
}
public Builder withJAXBContextFactory(JAXBContextFactory jaxbContextFactory) {
this.jaxbContextFactory = jaxbContextFactory;
return this;
}
public JAXBDecoder build() {
if (jaxbContextFactory == null) {
throw new IllegalStateException("JAXBContextFactory must be non-null");
}
return new JAXBDecoder(this);
}
}
}