Skip to content

Commit 2950c9f

Browse files
committed
[MNG-5862] Support XML entities and XInclude
1 parent 4a5b6c5 commit 2950c9f

File tree

13 files changed

+527
-68
lines changed

13 files changed

+527
-68
lines changed

.github/workflows/maven.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
cache: 'maven'
4848

4949
- name: Build with Maven
50-
run: mvn verify -e -B -V -DdistributionFileName=apache-maven
50+
run: mvn verify -e -B -V -DdistributionFileName=apache-maven -U
5151

5252
- name: Upload built Maven
5353
uses: actions/upload-artifact@v3

maven-core/src/main/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformer.java

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,17 @@
2121
import javax.annotation.PreDestroy;
2222
import javax.inject.Named;
2323
import javax.inject.Singleton;
24+
import javax.xml.stream.XMLInputFactory;
25+
import javax.xml.stream.XMLOutputFactory;
26+
import javax.xml.stream.XMLResolver;
2427
import javax.xml.stream.XMLStreamException;
2528
import javax.xml.stream.XMLStreamReader;
29+
import javax.xml.transform.Source;
2630

2731
import java.io.IOException;
2832
import java.io.InputStream;
33+
import java.net.URI;
34+
import java.net.URISyntaxException;
2935
import java.nio.file.Files;
3036
import java.nio.file.Path;
3137
import java.nio.file.Paths;
@@ -36,14 +42,17 @@
3642
import java.util.concurrent.CopyOnWriteArraySet;
3743
import java.util.function.BiConsumer;
3844

45+
import com.ctc.wstx.api.WstxInputProperties;
3946
import org.apache.maven.feature.Features;
4047
import org.apache.maven.model.building.DefaultBuildPomXMLFilterFactory;
4148
import org.apache.maven.model.building.TransformerContext;
4249
import org.apache.maven.model.transform.RawToConsumerPomXMLFilterFactory;
4350
import org.apache.maven.model.transform.stax.XmlUtils;
4451
import org.apache.maven.project.MavenProject;
4552
import org.apache.maven.project.artifact.ProjectArtifact;
53+
import org.apache.maven.stax.xinclude.XIncludeStreamReader;
4654
import org.codehaus.stax2.XMLInputFactory2;
55+
import org.codehaus.stax2.io.Stax2FileSource;
4756
import org.eclipse.aether.RepositorySystemSession;
4857
import org.eclipse.aether.artifact.Artifact;
4958
import org.eclipse.aether.artifact.DefaultArtifact;
@@ -183,13 +192,37 @@ private static BiConsumer<Path, Path> transformer(RepositorySystemSession sessio
183192
* The actual transformation: visible for testing.
184193
*/
185194
static InputStream transform(Path pomFile, TransformerContext context) throws IOException, XMLStreamException {
186-
try (InputStream input = Files.newInputStream(pomFile)) {
187-
XMLInputFactory2 factory = new com.ctc.wstx.stax.WstxInputFactory();
188-
factory.configureForRoundTripping();
189-
XMLStreamReader reader = factory.createXMLStreamReader(input);
190-
reader = new RawToConsumerPomXMLFilterFactory(new DefaultBuildPomXMLFilterFactory(context, true))
191-
.get(reader, pomFile);
192-
return XmlUtils.writeDocument(reader);
193-
}
195+
XMLInputFactory2 factory = new com.ctc.wstx.stax.WstxInputFactory();
196+
factory.configureForRoundTripping();
197+
factory.setProperty(WstxInputProperties.P_TREAT_CHAR_REFS_AS_ENTS, false);
198+
XMLResolver resolver = (String publicID, String systemID, String baseURI, String namespace) -> {
199+
if (systemID == null) {
200+
throw new XMLStreamException("systemID is null");
201+
}
202+
if (baseURI == null) {
203+
throw new XMLStreamException("baseURI is null");
204+
}
205+
URI sysUri;
206+
try {
207+
sysUri = new URI(systemID);
208+
} catch (URISyntaxException e) {
209+
throw new XMLStreamException("Invalid syntax for systemID uri: " + systemID, e);
210+
}
211+
if (sysUri.isAbsolute()) {
212+
throw new XMLStreamException("systemID must be a relative URL: " + systemID);
213+
}
214+
return URI.create(baseURI).resolve(sysUri);
215+
};
216+
factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, true);
217+
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
218+
factory.setProperty(WstxInputProperties.P_ENTITY_RESOLVER, resolver);
219+
Source source = new Stax2FileSource(pomFile.toFile());
220+
XMLStreamReader reader = factory.createXMLStreamReader(source);
221+
XMLOutputFactory outputFactory = new com.ctc.wstx.stax.WstxOutputFactory();
222+
reader =
223+
new XIncludeStreamReader(factory, outputFactory, pomFile.toUri().toString(), reader);
224+
reader = new RawToConsumerPomXMLFilterFactory(new DefaultBuildPomXMLFilterFactory(context, true))
225+
.get(reader, pomFile);
226+
return XmlUtils.writeDocument(reader);
194227
}
195228
}

maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ void testReadInvalidPom() throws Exception {
234234

235235
// single project build entry point
236236
Exception ex = assertThrows(Exception.class, () -> projectBuilder.build(pomFile, configuration));
237-
assertThat(ex.getMessage(), containsString("Received non-all-whitespace CHARACTERS or CDATA event"));
237+
assertThat(ex.getMessage(), containsString("expected START_TAG or END_TAG not CHARACTERS"));
238238

239239
// multi projects build entry point
240240
ProjectBuildingException pex = assertThrows(

maven-model-builder/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ under the License.
6565
<groupId>org.apache.maven</groupId>
6666
<artifactId>maven-model-transform</artifactId>
6767
</dependency>
68+
<dependency>
69+
<groupId>org.apache.maven.shared</groupId>
70+
<artifactId>stax-xinclude</artifactId>
71+
<version>1.0-SNAPSHOT</version>
72+
</dependency>
6873
<!-- Testing -->
6974
<dependency>
7075
<groupId>org.eclipse.sisu</groupId>

maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,9 +1008,15 @@ private org.apache.maven.api.model.Model doReadFileModel(
10081008
}
10091009

10101010
try {
1011-
model = modelProcessor
1012-
.read(modelSource.getInputStream(), options)
1013-
.getDelegate();
1011+
if (modelSource instanceof FileModelSource) {
1012+
model = modelProcessor
1013+
.read(((FileModelSource) modelSource).getFile(), options)
1014+
.getDelegate();
1015+
} else {
1016+
model = modelProcessor
1017+
.read(modelSource.getInputStream(), options)
1018+
.getDelegate();
1019+
}
10141020
} catch (ModelParseException e) {
10151021
if (!strict) {
10161022
throw e;
@@ -1019,9 +1025,15 @@ private org.apache.maven.api.model.Model doReadFileModel(
10191025
options.put(ModelProcessor.IS_STRICT, Boolean.FALSE);
10201026

10211027
try {
1022-
model = modelProcessor
1023-
.read(modelSource.getInputStream(), options)
1024-
.getDelegate();
1028+
if (modelSource instanceof FileModelSource) {
1029+
model = modelProcessor
1030+
.read(((FileModelSource) modelSource).getFile(), options)
1031+
.getDelegate();
1032+
} else {
1033+
model = modelProcessor
1034+
.read(modelSource.getInputStream(), options)
1035+
.getDelegate();
1036+
}
10251037
} catch (ModelParseException ne) {
10261038
// still unreadable even in non-strict mode, rethrow original error
10271039
throw e;

maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,31 @@
2323
import javax.inject.Singleton;
2424
import javax.xml.stream.Location;
2525
import javax.xml.stream.XMLInputFactory;
26+
import javax.xml.stream.XMLOutputFactory;
27+
import javax.xml.stream.XMLResolver;
2628
import javax.xml.stream.XMLStreamException;
2729
import javax.xml.stream.XMLStreamReader;
30+
import javax.xml.transform.stream.StreamSource;
2831

2932
import java.io.File;
3033
import java.io.IOException;
3134
import java.io.InputStream;
3235
import java.io.Reader;
36+
import java.net.URI;
37+
import java.net.URISyntaxException;
3338
import java.nio.file.Files;
3439
import java.nio.file.Path;
3540
import java.util.Map;
3641
import java.util.Objects;
3742

43+
import com.ctc.wstx.api.WstxInputProperties;
3844
import org.apache.maven.model.InputSource;
3945
import org.apache.maven.model.Model;
4046
import org.apache.maven.model.building.ModelSourceTransformer;
4147
import org.apache.maven.model.building.TransformerContext;
4248
import org.apache.maven.model.v4.MavenStaxReader;
49+
import org.apache.maven.stax.xinclude.XIncludeStreamReader;
50+
import org.codehaus.stax2.io.Stax2FileSource;
4351

4452
/**
4553
* Handles deserialization of a model from some kind of textual format like XML.
@@ -104,16 +112,48 @@ private TransformerContext getTransformerContext(Map<String, ?> options) {
104112

105113
private Model read(InputStream input, Path pomFile, Map<String, ?> options) throws IOException {
106114
try {
115+
InputSource source = getSource(options);
116+
boolean strict = isStrict(options);
107117
XMLInputFactory factory = new com.ctc.wstx.stax.WstxInputFactory();
108-
factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
109-
XMLStreamReader parser = factory.createXMLStreamReader(input);
118+
XMLOutputFactory outputFactory = new com.ctc.wstx.stax.WstxOutputFactory();
119+
120+
XMLStreamReader parser;
121+
// We only support xml entities and xinclude when reading a file in strict mode
122+
if (pomFile != null && strict) {
123+
XMLResolver resolver = (String publicID, String systemID, String baseURI, String namespace) -> {
124+
if (systemID == null) {
125+
throw new XMLStreamException("systemID is null");
126+
}
127+
if (baseURI == null) {
128+
throw new XMLStreamException("baseURI is null");
129+
}
130+
URI sysUri;
131+
try {
132+
sysUri = new URI(systemID);
133+
} catch (URISyntaxException e) {
134+
throw new XMLStreamException("Invalid syntax for systemID uri: " + systemID, e);
135+
}
136+
if (sysUri.isAbsolute()) {
137+
throw new XMLStreamException("systemID must be a relative URL: " + systemID);
138+
}
139+
return URI.create(baseURI).resolve(sysUri);
140+
};
141+
factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, true);
142+
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
143+
factory.setProperty(WstxInputProperties.P_ENTITY_RESOLVER, resolver);
144+
parser = new XIncludeStreamReader(
145+
factory,
146+
outputFactory,
147+
pomFile.toUri().toString(),
148+
factory.createXMLStreamReader(new StaxPathInputSource(pomFile, input)));
149+
} else {
150+
parser = factory.createXMLStreamReader(new StreamSource(input));
151+
}
110152

111153
TransformerContext context = getTransformerContext(options);
112154
XMLStreamReader transformingParser =
113155
context != null ? transformer.transform(parser, pomFile, context) : parser;
114156

115-
InputSource source = getSource(options);
116-
boolean strict = isStrict(options);
117157
if (source != null) {
118158
return readModelEx(transformingParser, source, strict);
119159
} else {
@@ -176,4 +216,18 @@ private Model readModelEx(XMLStreamReader parser, InputSource source, boolean st
176216
return new Model(mr.read(
177217
parser, strict, new org.apache.maven.api.model.InputSource(source.getModelId(), source.getLocation())));
178218
}
219+
220+
private static class StaxPathInputSource extends Stax2FileSource {
221+
private final InputStream input;
222+
223+
StaxPathInputSource(Path pomFile, InputStream input) {
224+
super(pomFile.toFile());
225+
this.input = input;
226+
}
227+
228+
@Override
229+
public InputStream constructInputStream() throws IOException {
230+
return input;
231+
}
232+
}
179233
}

maven-model-transform/src/main/java/org/apache/maven/model/transform/stax/BufferingParser.java

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@
3030
import java.util.Objects;
3131
import java.util.regex.Pattern;
3232

33+
import org.codehaus.stax2.DTDInfo;
3334
import org.codehaus.stax2.XMLStreamReader2;
35+
import org.codehaus.stax2.validation.DTDValidationSchema;
3436

35-
public class BufferingParser implements XMLStreamReader {
37+
public class BufferingParser implements XMLStreamReader, DTDInfo {
3638

3739
private static final Pattern WHITESPACE_REGEX = Pattern.compile("[ \r\t\n]+");
3840

@@ -90,6 +92,8 @@ public String toString() {
9092
case COMMENT:
9193
case SPACE:
9294
return "Event{event=" + TYPES[event] + ", text='" + text + "'}";
95+
case DTD:
96+
return "Event{event=" + TYPES[event] + ", text='" + text + "'}";
9397
case START_ELEMENT:
9498
return "Event{" + "event=START_TAG"
9599
+ ", name='"
@@ -396,6 +400,36 @@ public int getEventType() {
396400
return current != null ? current.event : delegate.getEventType();
397401
}
398402

403+
@Override
404+
public Object getProcessedDTD() {
405+
return delegate instanceof DTDInfo ? ((DTDInfo) delegate).getProcessedDTD() : null;
406+
}
407+
408+
@Override
409+
public String getDTDRootName() {
410+
return delegate instanceof DTDInfo ? ((DTDInfo) delegate).getDTDRootName() : null;
411+
}
412+
413+
@Override
414+
public String getDTDSystemId() {
415+
return delegate instanceof DTDInfo ? ((DTDInfo) delegate).getDTDSystemId() : null;
416+
}
417+
418+
@Override
419+
public String getDTDPublicId() {
420+
return delegate instanceof DTDInfo ? ((DTDInfo) delegate).getDTDPublicId() : null;
421+
}
422+
423+
@Override
424+
public String getDTDInternalSubset() {
425+
return delegate instanceof DTDInfo ? ((DTDInfo) delegate).getDTDInternalSubset() : null;
426+
}
427+
428+
@Override
429+
public DTDValidationSchema getProcessedDTDSchema() {
430+
return delegate instanceof DTDInfo ? ((DTDInfo) delegate).getProcessedDTDSchema() : null;
431+
}
432+
399433
@Override
400434
public int next() throws XMLStreamException {
401435
while (true) {
@@ -476,13 +510,20 @@ protected Event bufferEvent() throws XMLStreamException {
476510
event.name = pp.getLocalName();
477511
event.namespace = pp.getNamespaceURI();
478512
event.prefix = pp.getPrefix();
479-
// event.text = pp.getText();
480513
break;
481514
case CHARACTERS:
482515
case COMMENT:
483516
case SPACE:
517+
event.text = pp.getText();
518+
break;
519+
case DTD:
520+
event.text = pp.getText();
521+
break;
484522
case CDATA:
523+
event.text = pp.getText();
524+
break;
485525
case ENTITY_REFERENCE:
526+
event.name = pp.getLocalName();
486527
event.text = pp.getText();
487528
break;
488529
default:

maven-model-transform/src/main/java/org/apache/maven/model/transform/stax/XmlUtils.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.apache.maven.model.transform.stax;
2020

21+
import javax.xml.stream.XMLInputFactory;
2122
import javax.xml.stream.XMLOutputFactory;
2223
import javax.xml.stream.XMLStreamConstants;
2324
import javax.xml.stream.XMLStreamException;
@@ -101,20 +102,17 @@ public static void copy(XMLStreamReader reader, XMLStreamWriter writer, boolean
101102
elementCount = countStack.pop();
102103
}
103104
break;
104-
case XMLStreamConstants.CHARACTERS:
105-
writer.writeCharacters(normalize(reader.getText()));
105+
case XMLStreamConstants.PROCESSING_INSTRUCTION:
106+
writer.writeProcessingInstruction(reader.getPITarget(), reader.getPIData());
106107
break;
107-
case XMLStreamConstants.SPACE:
108+
case XMLStreamConstants.CHARACTERS:
108109
writer.writeCharacters(normalize(reader.getText()));
109110
break;
110-
case XMLStreamConstants.ENTITY_REFERENCE:
111-
writer.writeEntityRef(reader.getLocalName());
112-
break;
113111
case XMLStreamConstants.COMMENT:
114112
writer.writeComment(normalize(reader.getText()));
115113
break;
116-
case XMLStreamConstants.CDATA:
117-
writer.writeCData(normalize(reader.getText()));
114+
case XMLStreamConstants.SPACE:
115+
writer.writeCharacters(normalize(reader.getText()));
118116
break;
119117
case XMLStreamConstants.START_DOCUMENT:
120118
if (reader.getVersion() != null) {
@@ -124,6 +122,22 @@ public static void copy(XMLStreamReader reader, XMLStreamWriter writer, boolean
124122
case XMLStreamConstants.END_DOCUMENT:
125123
writer.writeEndDocument();
126124
return;
125+
case XMLStreamConstants.ENTITY_REFERENCE:
126+
if (!((Boolean) reader.getProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES))) {
127+
writer.writeEntityRef(reader.getLocalName());
128+
}
129+
break;
130+
case XMLStreamConstants.ATTRIBUTE:
131+
throw new IllegalStateException();
132+
case XMLStreamConstants.DTD:
133+
break;
134+
case XMLStreamConstants.CDATA:
135+
writer.writeCData(normalize(reader.getText()));
136+
break;
137+
case XMLStreamConstants.NAMESPACE:
138+
case XMLStreamConstants.NOTATION_DECLARATION:
139+
case XMLStreamConstants.ENTITY_DECLARATION:
140+
throw new IllegalStateException();
127141
default:
128142
break;
129143
}

0 commit comments

Comments
 (0)