Skip to content

Commit 0b146da

Browse files
authored
Merge pull request #300 from digipost/disable-xml-entity-expansions
Disable XML Entity Expansions for unmarshalling
2 parents 37c696b + a077a9b commit 0b146da

20 files changed

+952
-85
lines changed

jaxb/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106
<dependency>
107107
<groupId>nl.jqno.equalsverifier</groupId>
108108
<artifactId>equalsverifier</artifactId>
109-
<version>3.15.1</version>
109+
<version>3.15.3</version>
110110
<scope>test</scope>
111111
</dependency>
112112
<dependency>
@@ -193,7 +193,7 @@
193193
</plugin>
194194
<plugin>
195195
<artifactId>maven-javadoc-plugin</artifactId>
196-
<version>3.5.0</version>
196+
<version>3.6.0</version>
197197
<configuration>
198198
<detectOfflineLinks>false</detectOfflineLinks>
199199
<doclint>all,-missing,-html</doclint>

jaxb/src/main/java/no/digipost/signature/jaxb/JaxbMarshaller.java

Lines changed: 38 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -16,38 +16,26 @@
1616
package no.digipost.signature.jaxb;
1717

1818
import no.digipost.signature.xsd.SignatureApiSchemas;
19+
import no.digipost.xml.bind.MarshallingCustomization;
20+
import no.digipost.xml.parsers.SaxParserProvider;
21+
import no.digipost.xml.transform.sax.SaxInputSources;
1922
import org.w3c.dom.Document;
20-
import org.xml.sax.InputSource;
21-
import org.xml.sax.XMLReader;
23+
import org.w3c.dom.Node;
2224

23-
import javax.xml.XMLConstants;
2425
import javax.xml.bind.JAXBContext;
25-
import javax.xml.bind.JAXBException;
2626
import javax.xml.bind.Marshaller;
2727
import javax.xml.bind.Unmarshaller;
28-
import javax.xml.parsers.SAXParser;
29-
import javax.xml.parsers.SAXParserFactory;
3028
import javax.xml.transform.Source;
3129
import javax.xml.transform.dom.DOMResult;
32-
import javax.xml.transform.sax.SAXSource;
33-
import javax.xml.validation.Schema;
34-
import javax.xml.validation.SchemaFactory;
3530

3631
import java.io.ByteArrayInputStream;
3732
import java.io.ByteArrayOutputStream;
38-
import java.io.IOException;
3933
import java.io.InputStream;
4034
import java.io.OutputStream;
41-
import java.io.UncheckedIOException;
42-
import java.net.URL;
43-
import java.util.Collection;
44-
import java.util.Optional;
4535
import java.util.Set;
46-
import java.util.stream.Stream;
4736

4837
import static java.nio.charset.StandardCharsets.UTF_8;
49-
import static java.util.Objects.requireNonNull;
50-
import static java.util.stream.Collectors.joining;
38+
import static no.digipost.xml.bind.MarshallingCustomization.validateUsingSchemaResources;
5139

5240
/**
5341
* @see JaxbMarshaller.ForResponsesOfAllApis
@@ -71,7 +59,7 @@ private static final class SingletonHolder {
7159
}
7260

7361
public ForRequestsOfAllApis() {
74-
super(SignatureMarshalling.allApiRequestClasses(), SignatureApiSchemas.DIRECT_AND_PORTAL_API);
62+
super(validateUsingSchemaResources(SignatureApiSchemas.DIRECT_AND_PORTAL_API), SignatureMarshalling.allApiRequestClasses());
7563
}
7664
}
7765

@@ -91,7 +79,7 @@ private static final class SingletonHolder {
9179
}
9280

9381
public ForResponsesOfAllApis() {
94-
super(SignatureMarshalling.allApiResponseClasses());
82+
super(MarshallingCustomization.NO_CUSTOMIZATION, SignatureMarshalling.allApiResponseClasses());
9583
}
9684
}
9785

@@ -119,72 +107,42 @@ private static final class SingletonHolder {
119107
}
120108

121109
public ForAllApis() {
122-
super(SignatureMarshalling.allApiClasses(), SignatureApiSchemas.DIRECT_AND_PORTAL_API);
110+
super(validateUsingSchemaResources(SignatureApiSchemas.DIRECT_AND_PORTAL_API), SignatureMarshalling.allApiClasses());
123111
}
124112
}
125113

126-
private static InputSource createInputSource(String resource) {
127-
URL resourceUrl = requireNonNull(JaxbMarshaller.class.getResource(resource), resource);
128-
try (InputStream inputStream = resourceUrl.openStream()) {
129-
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
130-
131-
final int bufLen = 1024;
132-
byte[] buf = new byte[bufLen];
133-
int readLen;
134-
while ((readLen = inputStream.read(buf, 0, bufLen)) != -1)
135-
outputStream.write(buf, 0, readLen);
136-
137-
InputSource source = new InputSource(new ByteArrayInputStream(outputStream.toByteArray()));
138-
source.setSystemId(resourceUrl.toString());
139-
return source;
140-
} catch (IOException e) {
141-
throw new UncheckedIOException(
142-
"Unable to resolve " + resource + " from " + resourceUrl + ", " +
143-
"because " + e.getClass().getSimpleName() + " '" + e.getMessage() + "'", e);
144-
}
145-
}
114+
private final JAXBContext jaxbContext;
115+
private final MarshallingCustomization marshallingCustomization;
116+
private final SaxParserProvider saxParserProvider;
146117

147-
private static Schema createSchema(Collection<String> resources) {
148-
try {
149-
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
150-
parserFactory.setNamespaceAware(true);
151-
parserFactory.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
152-
153-
SAXParser saxParser = parserFactory.newSAXParser();
154-
XMLReader xmlReader = saxParser.getXMLReader();
155-
Source[] schemaSources = resources.stream()
156-
.map(resource -> new SAXSource(xmlReader, createInputSource(resource)))
157-
.toArray(Source[]::new);
158-
159-
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
160-
Schema schema = schemaFactory.newSchema(schemaSources);
161-
return schema;
162-
} catch (Exception e) {
163-
throw new RuntimeException("Could not create schema from resources [" + String.join(", ", resources) + "]", e);
164-
}
118+
public JaxbMarshaller(MarshallingCustomization marshallingCustomization, Class<?> ... classesToBeBound) {
119+
this.jaxbContext = JaxbUtils.initContext(classesToBeBound);
120+
this.marshallingCustomization = marshallingCustomization;
121+
this.saxParserProvider = SaxParserProvider.createSecuredProvider();
165122
}
166123

167-
private static JAXBContext initContext(Collection<Class<?>> classes) {
168-
Class<?>[] classesToBeBound = classes.toArray(new Class[classes.size()]);
169-
try {
170-
return JAXBContext.newInstance(classesToBeBound);
171-
} catch (JAXBException e) {
172-
throw new RuntimeException("Could not create JAXBContext for classes [" + Stream.of(classesToBeBound).map(Class::getSimpleName).collect(joining(",")) + "]" , e);
173-
}
124+
public JaxbMarshaller(MarshallingCustomization marshallingCustomization, Set<Class<?>> classesToBeBound) {
125+
this(marshallingCustomization, classesToBeBound.toArray(new Class[classesToBeBound.size()]));
174126
}
175127

176-
private final JAXBContext jaxbContext;
177-
private final Optional<Schema> schema;
128+
public JaxbMarshaller(Class<?> ... classesToBeBound) {
129+
this(MarshallingCustomization.NO_CUSTOMIZATION, classesToBeBound);
130+
}
178131

179-
public JaxbMarshaller(Set<Class<?>> classes, Set<String> schemaResources) {
180-
this.jaxbContext = initContext(classes);
181-
this.schema = Optional.ofNullable(schemaResources).filter(s -> !s.isEmpty()).map(JaxbMarshaller::createSchema);
132+
public JaxbMarshaller(Set<Class<?>> classesToBeBound) {
133+
this(MarshallingCustomization.NO_CUSTOMIZATION, classesToBeBound);
182134
}
183135

184-
public JaxbMarshaller(Set<Class<?>> classes) {
185-
this(classes, null);
136+
/**
137+
* @deprecated Use {@link #JaxbMarshaller(MarshallingCustomization, Set)} with
138+
* {@link MarshallingCustomization#validateUsingSchemaResources(Set)}
139+
*/
140+
@Deprecated
141+
public JaxbMarshaller(Set<Class<?>> classesToBeBound, Set<String> schemaResources) {
142+
this(MarshallingCustomization.validateUsingSchemaResources(schemaResources), classesToBeBound);
186143
}
187144

145+
188146
public String marshalToString(Object object) {
189147
return marshalToResult(object, xml -> xml.toString(UTF_8.name()));
190148
}
@@ -228,26 +186,30 @@ private interface ThrowingBiConsumer<T, S> {
228186
private <T> void doWithMarshaller(T object, ThrowingBiConsumer<? super T, ? super Marshaller> operation) {
229187
try {
230188
Marshaller marshaller = jaxbContext.createMarshaller();
231-
schema.ifPresent(marshaller::setSchema);
189+
marshallingCustomization.customize(marshaller);
232190
operation.accept(object, marshaller);
233191
} catch (Exception e) {
234192
throw SignatureMarshalException.failedMarshal(object, e);
235193
}
236194
}
237195

238-
239196
public <T> T unmarshal(InputStream inputStream, Class<T> type) {
240-
return unmarshal(unmarshaller -> unmarshaller.unmarshal(inputStream), type);
197+
Source xmlSource = saxParserProvider.createSource(SaxInputSources.fromInputStreamPreventClose(inputStream));
198+
return unmarshal(unmarshaller -> unmarshaller.unmarshal(xmlSource), type);
241199
}
242200

243201
public <T> T unmarshal(byte[] bytes, Class<T> type) {
244202
return unmarshal(new ByteArrayInputStream(bytes), type);
245203
}
246204

205+
public <T> T unmarshal(Node node, Class<T> type) {
206+
return unmarshal(unmarshaller -> unmarshaller.unmarshal(node), type);
207+
}
208+
247209
private <T> T unmarshal(ThrowingFunction<? super Unmarshaller, ?> operation, Class<T> type) {
248210
try {
249211
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
250-
schema.ifPresent(unmarshaller::setSchema);
212+
marshallingCustomization.customize(unmarshaller);
251213
return type.cast(operation.apply(unmarshaller));
252214
} catch (Exception e) {
253215
throw SignatureMarshalException.failedUnmarshal(type, e);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (C) Posten Norge AS
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package no.digipost.signature.jaxb;
17+
18+
import javax.xml.bind.JAXBContext;
19+
import javax.xml.bind.JAXBException;
20+
21+
import java.util.stream.Stream;
22+
23+
import static java.util.stream.Collectors.joining;
24+
25+
final class JaxbUtils {
26+
27+
public static JAXBContext initContext(Class<?> ... classesToBeBound) {
28+
try {
29+
return JAXBContext.newInstance(classesToBeBound);
30+
} catch (JAXBException e) {
31+
throw new RuntimeException(
32+
"Could not create JAXBContext for classes [" + Stream.of(classesToBeBound).map(Class::getSimpleName).collect(joining(",")) + "] " +
33+
"because " + e.getClass().getSimpleName() + " '" + e.getMessage() + "'", e);
34+
}
35+
}
36+
37+
private JaxbUtils() {
38+
}
39+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (C) Posten Norge AS
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package no.digipost.xml.bind;
17+
18+
import no.digipost.xml.validation.SchemaHelper;
19+
20+
import javax.xml.bind.Marshaller;
21+
import javax.xml.validation.Schema;
22+
23+
import java.util.Optional;
24+
import java.util.Set;
25+
26+
@FunctionalInterface
27+
public interface MarshallerCustomizer {
28+
29+
final MarshallerCustomizer NO_CUSTOMIZATION = new NoCustomization("MarshallerCustomizer.NO_CUSTOMIZATION");
30+
31+
public static MarshallerCustomizer validateUsingSchemaResources(Set<String> schemaResources) {
32+
return Optional.ofNullable(schemaResources)
33+
.filter(s -> !s.isEmpty())
34+
.map(SchemaHelper::createW3cXmlSchema)
35+
.map(MarshallerCustomizer::validateUsingSchema)
36+
.orElse(NO_CUSTOMIZATION);
37+
}
38+
39+
public static MarshallerCustomizer validateUsingSchema(Schema schema) {
40+
return marshaller -> marshaller.setSchema(schema);
41+
}
42+
43+
44+
45+
void customize(Marshaller marshaller) throws Exception;
46+
47+
48+
default MarshallerCustomizer andThen(MarshallerCustomizer nextCustomization) {
49+
if (nextCustomization instanceof NoCustomization) {
50+
return this;
51+
} else {
52+
return marshaller -> {
53+
this.customize(marshaller);
54+
nextCustomization.customize(marshaller);
55+
};
56+
}
57+
}
58+
59+
}

0 commit comments

Comments
 (0)