Skip to content

Commit c7662cb

Browse files
committed
[MNG-7836] Support alternative syntaxes for POMs
# Conflicts: # maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java
1 parent 25488f9 commit c7662cb

File tree

11 files changed

+399
-41
lines changed

11 files changed

+399
-41
lines changed

.github/workflows/maven.yml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,19 +106,21 @@ jobs:
106106
ref: ${{ env.REPO_BRANCH }}
107107
persist-credentials: false
108108

109-
- name: Download built Maven
110-
uses: actions/download-artifact@v3
111-
with:
112-
name: built-maven
113-
path: built-maven/
114-
115109
- name: Set up JDK
116110
uses: actions/setup-java@v3
117111
with:
118112
java-version: ${{ matrix.java }}
119113
distribution: 'temurin'
120114
# cache: 'maven' - don't use cache for integration tests
121115

116+
- uses: actions/checkout@v3
117+
with:
118+
path: maven/
119+
persist-credentials: false
120+
121+
- name: Build Maven
122+
run: mvn install -e -B -V -DdistributionFileName=apache-maven -DskipTests -f maven/pom.xml
123+
122124
- name: Running integration tests
123125
shell: bash
124-
run: mvn install -e -B -V -Prun-its,embedded -DmavenDistro="$GITHUB_WORKSPACE/built-maven/apache-maven-bin.zip" -f maven-integration-testing/pom.xml
126+
run: mvn install -e -B -V -Prun-its,embedded -DmavenDistro="$GITHUB_WORKSPACE/maven/apache-maven/target/apache-maven-bin.zip" -f maven-integration-testing/pom.xml
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.api.services;
20+
21+
import java.io.IOException;
22+
import java.io.InputStream;
23+
import java.nio.file.Files;
24+
import java.nio.file.Path;
25+
import java.util.Objects;
26+
27+
import org.apache.maven.api.annotations.Nonnull;
28+
29+
public class PathSource implements Source {
30+
31+
private final Path path;
32+
33+
public PathSource(@Nonnull Path path) {
34+
this.path = Objects.requireNonNull(path);
35+
}
36+
37+
@Nonnull
38+
@Override
39+
public Path getPath() {
40+
return path;
41+
}
42+
43+
@Nonnull
44+
@Override
45+
public InputStream openStream() throws IOException {
46+
return Files.newInputStream(path);
47+
}
48+
49+
@Nonnull
50+
@Override
51+
public String getLocation() {
52+
return path.toString();
53+
}
54+
}

api/maven-api-model/pom.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ under the License.
7676
</excludes>
7777
</configuration>
7878
</plugin>
79+
<plugin>
80+
<groupId>org.codehaus.mojo</groupId>
81+
<artifactId>build-helper-maven-plugin</artifactId>
82+
<version>3.4.0</version>
83+
<executions>
84+
<execution>
85+
<goals>
86+
<goal>attach-artifact</goal>
87+
</goals>
88+
<configuration>
89+
<artifacts>
90+
<artifact>
91+
<file>${basedir}/src/main/mdo/maven.mdo</file>
92+
<type>mdo</type>
93+
</artifact>
94+
</artifacts>
95+
</configuration>
96+
</execution>
97+
</executions>
98+
</plugin>
7999
</plugins>
80100
</build>
81101

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.api.spi;
20+
21+
import java.nio.file.Path;
22+
import java.util.Map;
23+
import java.util.Optional;
24+
25+
import org.apache.maven.api.annotations.Experimental;
26+
import org.apache.maven.api.annotations.Nonnull;
27+
import org.apache.maven.api.annotations.Nullable;
28+
import org.apache.maven.api.model.Model;
29+
import org.apache.maven.api.services.Source;
30+
31+
/**
32+
* The {@code ModelParser} interface is used to locate and read {@link Model}s from the file system.
33+
* This allows plugging in additional syntaxes for the main model read by Maven when building a project.
34+
*/
35+
@Experimental
36+
public interface ModelParser {
37+
38+
/**
39+
* Locates the pom in the given directory.
40+
*
41+
* @param dir the directory to locate the pom for, never {@code null}
42+
* @return a {@code Source} pointing to the located pom or an empty {@code Optional} if none was found by this parser
43+
*/
44+
@Nonnull
45+
Optional<Source> locate(@Nonnull Path dir);
46+
47+
/**
48+
* Parse the model obtained previously by a previous call to {@link #locate(Path)}.
49+
*
50+
* @param source the source to parse, never {@code null}
51+
* @param options possible parsing options, may be {@code null}
52+
* @return the parsed {@link Model}, never {@code null}
53+
* @throws ModelParserException if the model cannot be parsed
54+
*/
55+
@Nonnull
56+
Model parse(@Nonnull Source source, @Nullable Map<String, ?> options) throws ModelParserException;
57+
58+
/**
59+
* Locate and parse the model in the specified directory.
60+
* This is equivalent to {@code locate(dir).map(s -> parse(s, options))}.
61+
*
62+
* @param dir the directory to locate the pom for, never {@code null}
63+
* @param options possible parsing options, may be {@code null}
64+
* @return an optional parsed {@link Model} or {@code null} if none could be found
65+
* @throws ModelParserException if the located model cannot be parsed
66+
*/
67+
default Optional<Model> locateAndParse(@Nonnull Path dir, @Nullable Map<String, ?> options)
68+
throws ModelParserException {
69+
return locate(dir).map(s -> parse(s, options));
70+
}
71+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.api.spi;
20+
21+
import org.apache.maven.api.annotations.Experimental;
22+
import org.apache.maven.api.services.MavenException;
23+
24+
@Experimental
25+
public class ModelParserException extends MavenException {
26+
27+
/**
28+
* The one-based index of the line containing the error.
29+
*/
30+
private final int lineNumber;
31+
32+
/**
33+
* The one-based index of the column containing the error.
34+
*/
35+
private final int columnNumber;
36+
37+
public ModelParserException() {
38+
this(null, null);
39+
}
40+
41+
public ModelParserException(String message) {
42+
this(message, null);
43+
}
44+
45+
public ModelParserException(String message, Throwable cause) {
46+
this(message, -1, -1, cause);
47+
}
48+
49+
public ModelParserException(String message, int lineNumber, int columnNumber, Throwable cause) {
50+
super(message, cause);
51+
this.lineNumber = lineNumber;
52+
this.columnNumber = columnNumber;
53+
}
54+
55+
public ModelParserException(Throwable cause) {
56+
this(null, cause);
57+
}
58+
59+
public int getLineNumber() {
60+
return lineNumber;
61+
}
62+
63+
public int getColumnNumber() {
64+
return columnNumber;
65+
}
66+
}

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

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,34 @@
1919
package org.apache.maven.internal.transformation;
2020

2121
import javax.annotation.PreDestroy;
22+
import javax.inject.Inject;
2223
import javax.inject.Named;
2324
import javax.inject.Singleton;
2425
import javax.xml.stream.XMLStreamException;
2526
import javax.xml.stream.XMLStreamReader;
2627

2728
import java.io.IOException;
2829
import java.io.InputStream;
30+
import java.io.OutputStream;
2931
import java.nio.file.Files;
3032
import java.nio.file.Path;
3133
import java.nio.file.Paths;
32-
import java.nio.file.StandardCopyOption;
3334
import java.util.ArrayList;
3435
import java.util.Collection;
36+
import java.util.Map;
3537
import java.util.Set;
3638
import java.util.concurrent.CopyOnWriteArraySet;
3739
import java.util.function.BiConsumer;
3840

3941
import org.apache.maven.api.feature.Features;
42+
import org.apache.maven.api.model.Model;
43+
import org.apache.maven.api.services.Source;
44+
import org.apache.maven.api.spi.ModelParser;
4045
import org.apache.maven.model.building.DefaultBuildPomXMLFilterFactory;
4146
import org.apache.maven.model.building.TransformerContext;
4247
import org.apache.maven.model.transform.RawToConsumerPomXMLFilterFactory;
4348
import org.apache.maven.model.transform.stax.XmlUtils;
49+
import org.apache.maven.model.v4.MavenStaxWriter;
4450
import org.apache.maven.project.MavenProject;
4551
import org.apache.maven.project.artifact.ProjectArtifact;
4652
import org.codehaus.stax2.XMLInputFactory2;
@@ -63,6 +69,13 @@ public final class ConsumerPomArtifactTransformer {
6369

6470
private final Set<Path> toDelete = new CopyOnWriteArraySet<>();
6571

72+
private final Map<String, ModelParser> modelParsers;
73+
74+
@Inject
75+
ConsumerPomArtifactTransformer(Map<String, ModelParser> modelParsers) {
76+
this.modelParsers = modelParsers;
77+
}
78+
6679
public void injectTransformedArtifacts(MavenProject project, RepositorySystemSession session) throws IOException {
6780
if (project.getFile() == null) {
6881
// If there is no build POM there is no reason to inject artifacts for the consumer POM.
@@ -150,7 +163,7 @@ private Collection<Artifact> replacePom(Collection<Artifact> artifacts) {
150163
/**
151164
* Consumer POM is transformed from original POM.
152165
*/
153-
private static class ConsumerPomArtifact extends TransformedArtifact {
166+
private class ConsumerPomArtifact extends TransformedArtifact {
154167

155168
private ConsumerPomArtifact(MavenProject mavenProject, Path target, RepositorySystemSession session) {
156169
super(
@@ -161,31 +174,56 @@ private ConsumerPomArtifact(MavenProject mavenProject, Path target, RepositorySy
161174
target,
162175
transformer(session));
163176
}
177+
}
164178

165-
private static BiConsumer<Path, Path> transformer(RepositorySystemSession session) {
166-
TransformerContext context = (TransformerContext) session.getData().get(TransformerContext.KEY);
167-
return (src, dest) -> {
168-
try (InputStream inputStream = transform(src, context)) {
169-
Files.createDirectories(dest.getParent());
170-
Files.copy(inputStream, dest, StandardCopyOption.REPLACE_EXISTING);
171-
} catch (XMLStreamException | IOException e) {
172-
throw new RuntimeException(e);
173-
}
174-
};
175-
}
179+
BiConsumer<Path, Path> transformer(RepositorySystemSession session) {
180+
TransformerContext context = (TransformerContext) session.getData().get(TransformerContext.KEY);
181+
return (src, dest) -> {
182+
try {
183+
Files.createDirectories(dest.getParent());
184+
transform(src, dest, context);
185+
} catch (XMLStreamException | IOException e) {
186+
throw new RuntimeException(e);
187+
}
188+
};
176189
}
177190

178191
/**
179192
* The actual transformation: visible for testing.
180193
*/
181-
static InputStream transform(Path pomFile, TransformerContext context) throws IOException, XMLStreamException {
182-
try (InputStream input = Files.newInputStream(pomFile)) {
183-
XMLInputFactory2 factory = new com.ctc.wstx.stax.WstxInputFactory();
184-
factory.configureForRoundTripping();
185-
XMLStreamReader reader = factory.createXMLStreamReader(input);
186-
reader = new RawToConsumerPomXMLFilterFactory(new DefaultBuildPomXMLFilterFactory(context, true))
187-
.get(reader, pomFile);
188-
return XmlUtils.writeDocument(reader);
194+
void transform(Path pomFile, Path destPomFile, TransformerContext context) throws IOException, XMLStreamException {
195+
ModelParser parser = null;
196+
Source source = null;
197+
for (ModelParser p : modelParsers.values()) {
198+
source = p.locate(pomFile).orElse(null);
199+
if (source != null) {
200+
parser = p;
201+
break;
202+
}
203+
}
204+
205+
if (source != null) {
206+
try (OutputStream output = Files.newOutputStream(destPomFile)) {
207+
Model model = parser.parse(source, null);
208+
// transform
209+
model = model.withRoot(false).withModules(null);
210+
if (model.getParent() != null) {
211+
model = model.withParent(model.getParent().withRelativePath(null));
212+
}
213+
MavenStaxWriter w = new MavenStaxWriter();
214+
w.write(output, model);
215+
}
216+
} else {
217+
try (OutputStream output = Files.newOutputStream(destPomFile)) {
218+
try (InputStream input = Files.newInputStream(pomFile)) {
219+
XMLInputFactory2 factory = new com.ctc.wstx.stax.WstxInputFactory();
220+
factory.configureForRoundTripping();
221+
XMLStreamReader reader = factory.createXMLStreamReader(input);
222+
reader = new RawToConsumerPomXMLFilterFactory(new DefaultBuildPomXMLFilterFactory(context, true))
223+
.get(reader, pomFile);
224+
XmlUtils.writeDocument(reader, output);
225+
}
226+
}
189227
}
190228
}
191229
}

0 commit comments

Comments
 (0)