View Javadoc
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.project;
20  
21  import java.io.File;
22  import java.nio.charset.StandardCharsets;
23  import java.nio.file.Files;
24  import java.nio.file.Path;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.List;
28  import java.util.Properties;
29  import java.util.concurrent.atomic.AtomicInteger;
30  
31  import org.apache.maven.AbstractCoreMavenComponentTestCase;
32  import org.apache.maven.execution.MavenSession;
33  import org.apache.maven.model.Dependency;
34  import org.apache.maven.model.InputLocation;
35  import org.apache.maven.model.Plugin;
36  import org.apache.maven.model.building.FileModelSource;
37  import org.apache.maven.model.building.ModelBuildingRequest;
38  import org.apache.maven.model.building.ModelProblem;
39  import org.apache.maven.model.building.ModelSource;
40  import org.codehaus.plexus.util.FileUtils;
41  import org.junit.jupiter.api.Test;
42  import org.junit.jupiter.api.io.TempDir;
43  
44  import static org.apache.maven.project.ProjectBuildingResultWithLocationMatcher.projectBuildingResultWithLocation;
45  import static org.apache.maven.project.ProjectBuildingResultWithProblemMessageMatcher.projectBuildingResultWithProblemMessage;
46  import static org.hamcrest.MatcherAssert.assertThat;
47  import static org.hamcrest.Matchers.contains;
48  import static org.hamcrest.Matchers.containsString;
49  import static org.hamcrest.Matchers.empty;
50  import static org.hamcrest.Matchers.greaterThan;
51  import static org.hamcrest.Matchers.hasKey;
52  import static org.hamcrest.Matchers.is;
53  import static org.junit.jupiter.api.Assertions.assertEquals;
54  import static org.junit.jupiter.api.Assertions.assertNotNull;
55  import static org.junit.jupiter.api.Assertions.assertThrows;
56  import static org.junit.jupiter.api.Assertions.assertTrue;
57  
58  class ProjectBuilderTest extends AbstractCoreMavenComponentTestCase {
59      @Override
60      protected String getProjectsDirectory() {
61          return "src/test/projects/project-builder";
62      }
63  
64      @Test
65      void testSystemScopeDependencyIsPresentInTheCompileClasspathElements() throws Exception {
66          File pom = getProject("it0063");
67  
68          Properties eps = new Properties();
69          eps.setProperty("jre.home", new File(pom.getParentFile(), "jdk/jre").getPath());
70  
71          MavenSession session = createMavenSession(pom, eps);
72          MavenProject project = session.getCurrentProject();
73  
74          // Here we will actually not have any artifacts because the ProjectDependenciesResolver is not involved here. So
75          // right now it's not valid to ask for artifacts unless plugins require the artifacts.
76  
77          project.getCompileClasspathElements();
78      }
79  
80      @Test
81      void testBuildFromModelSource() throws Exception {
82          File pomFile = new File("src/test/resources/projects/modelsource/module01/pom.xml");
83          MavenSession mavenSession = createMavenSession(pomFile);
84          ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
85          configuration.setRepositorySession(mavenSession.getRepositorySession());
86          ModelSource modelSource = new FileModelSource(pomFile);
87          ProjectBuildingResult result = getContainer()
88                  .lookup(org.apache.maven.project.ProjectBuilder.class)
89                  .build(modelSource, configuration);
90  
91          assertNotNull(result.getProject().getParentFile());
92      }
93  
94      @Test
95      void testVersionlessManagedDependency() throws Exception {
96          File pomFile = new File("src/test/resources/projects/versionless-managed-dependency.xml");
97          MavenSession mavenSession = createMavenSession(null);
98          ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
99          configuration.setRepositorySession(mavenSession.getRepositorySession());
100 
101         ProjectBuildingException e = assertThrows(ProjectBuildingException.class, () -> getContainer()
102                 .lookup(org.apache.maven.project.ProjectBuilder.class)
103                 .build(pomFile, configuration));
104         assertThat(
105                 e.getResults(),
106                 contains(projectBuildingResultWithProblemMessage(
107                         "'dependencies.dependency.version' for org.apache.maven.its:a:jar is missing")));
108         assertThat(e.getResults(), contains(projectBuildingResultWithLocation(5, 9)));
109     }
110 
111     @Test
112     void testResolveDependencies() throws Exception {
113         File pomFile = new File("src/test/resources/projects/basic-resolveDependencies.xml");
114         MavenSession mavenSession = createMavenSession(null);
115         ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
116         configuration.setRepositorySession(mavenSession.getRepositorySession());
117         configuration.setResolveDependencies(true);
118 
119         // single project build entry point
120         ProjectBuildingResult result = getContainer()
121                 .lookup(org.apache.maven.project.ProjectBuilder.class)
122                 .build(pomFile, configuration);
123         assertEquals(1, result.getProject().getArtifacts().size());
124         // multi projects build entry point
125         List<ProjectBuildingResult> results = getContainer()
126                 .lookup(org.apache.maven.project.ProjectBuilder.class)
127                 .build(Collections.singletonList(pomFile), false, configuration);
128         assertEquals(1, results.size());
129         MavenProject mavenProject = results.get(0).getProject();
130         assertEquals(1, mavenProject.getArtifacts().size());
131 
132         final MavenProject project = mavenProject;
133         final AtomicInteger artifactsResultInAnotherThread = new AtomicInteger();
134         Thread t = new Thread(new Runnable() {
135             @Override
136             public void run() {
137                 artifactsResultInAnotherThread.set(project.getArtifacts().size());
138             }
139         });
140         t.start();
141         t.join();
142         assertEquals(project.getArtifacts().size(), artifactsResultInAnotherThread.get());
143     }
144 
145     @Test
146     void testDontResolveDependencies() throws Exception {
147         File pomFile = new File("src/test/resources/projects/basic-resolveDependencies.xml");
148         MavenSession mavenSession = createMavenSession(null);
149         ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
150         configuration.setRepositorySession(mavenSession.getRepositorySession());
151         configuration.setResolveDependencies(false);
152 
153         // single project build entry point
154         ProjectBuildingResult result = getContainer()
155                 .lookup(org.apache.maven.project.ProjectBuilder.class)
156                 .build(pomFile, configuration);
157         assertEquals(0, result.getProject().getArtifacts().size());
158         // multi projects build entry point
159         List<ProjectBuildingResult> results = getContainer()
160                 .lookup(org.apache.maven.project.ProjectBuilder.class)
161                 .build(Collections.singletonList(pomFile), false, configuration);
162         assertEquals(1, results.size());
163         MavenProject mavenProject = results.get(0).getProject();
164         assertEquals(0, mavenProject.getArtifacts().size());
165     }
166 
167     @Test
168     void testReadModifiedPoms(@TempDir Path tempDir) throws Exception {
169         // TODO a similar test should be created to test the dependency management (basically all usages
170         // of DefaultModelBuilder.getCache() are affected by MNG-6530
171 
172         FileUtils.copyDirectoryStructure(new File("src/test/resources/projects/grandchild-check"), tempDir.toFile());
173 
174         MavenSession mavenSession = createMavenSession(null);
175         ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
176         configuration.setRepositorySession(mavenSession.getRepositorySession());
177         org.apache.maven.project.ProjectBuilder projectBuilder =
178                 getContainer().lookup(org.apache.maven.project.ProjectBuilder.class);
179         File child = new File(tempDir.toFile(), "child/pom.xml");
180         // build project once
181         projectBuilder.build(child, configuration);
182         // modify parent
183         File parent = new File(tempDir.toFile(), "pom.xml");
184         String parentContent = new String(Files.readAllBytes(parent.toPath()), StandardCharsets.UTF_8);
185         parentContent = parentContent.replace(
186                 "<packaging>pom</packaging>",
187                 "<packaging>pom</packaging><properties><addedProperty>addedValue</addedProperty></properties>");
188         Files.write(parent.toPath(), parentContent.getBytes(StandardCharsets.UTF_8));
189         // re-build pom with modified parent
190         ProjectBuildingResult result = projectBuilder.build(child, configuration);
191         assertThat(result.getProject().getProperties(), hasKey((Object) "addedProperty"));
192     }
193 
194     @Test
195     void testReadErroneousMavenProjectContainsReference() throws Exception {
196         File pomFile = new File("src/test/resources/projects/artifactMissingVersion/pom.xml").getAbsoluteFile();
197         MavenSession mavenSession = createMavenSession(null);
198         mavenSession.getRequest().setRootDirectory(pomFile.getParentFile().toPath());
199         ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
200         configuration.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
201         configuration.setRepositorySession(mavenSession.getRepositorySession());
202         org.apache.maven.project.ProjectBuilder projectBuilder =
203                 getContainer().lookup(org.apache.maven.project.ProjectBuilder.class);
204 
205         // single project build entry point
206         ProjectBuildingException ex1 =
207                 assertThrows(ProjectBuildingException.class, () -> projectBuilder.build(pomFile, configuration));
208 
209         assertEquals(1, ex1.getResults().size());
210         MavenProject project1 = ex1.getResults().get(0).getProject();
211         assertNotNull(project1);
212         assertEquals("testArtifactMissingVersion", project1.getArtifactId());
213         assertEquals(pomFile, project1.getFile());
214 
215         // multi projects build entry point
216         ProjectBuildingException ex2 = assertThrows(
217                 ProjectBuildingException.class,
218                 () -> projectBuilder.build(Collections.singletonList(pomFile), false, configuration));
219 
220         assertEquals(1, ex2.getResults().size());
221         MavenProject project2 = ex2.getResults().get(0).getProject();
222         assertNotNull(project2);
223         assertEquals("testArtifactMissingVersion", project2.getArtifactId());
224         assertEquals(pomFile, project2.getFile());
225     }
226 
227     @Test
228     void testReadInvalidPom() throws Exception {
229         File pomFile = new File("src/test/resources/projects/badPom.xml").getAbsoluteFile();
230         MavenSession mavenSession = createMavenSession(null);
231         ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
232         configuration.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_STRICT);
233         configuration.setRepositorySession(mavenSession.getRepositorySession());
234         org.apache.maven.project.ProjectBuilder projectBuilder =
235                 getContainer().lookup(org.apache.maven.project.ProjectBuilder.class);
236 
237         // single project build entry point
238         Exception ex = assertThrows(Exception.class, () -> projectBuilder.build(pomFile, configuration));
239         assertThat(ex.getMessage(), containsString("Received non-all-whitespace CHARACTERS or CDATA event"));
240 
241         // multi projects build entry point
242         ProjectBuildingException pex = assertThrows(
243                 ProjectBuildingException.class,
244                 () -> projectBuilder.build(Collections.singletonList(pomFile), false, configuration));
245         assertEquals(1, pex.getResults().size());
246         assertNotNull(pex.getResults().get(0).getPomFile());
247         assertThat(pex.getResults().get(0).getProblems().size(), greaterThan(0));
248         assertThat(
249                 pex.getResults(),
250                 contains(projectBuildingResultWithProblemMessage(
251                         "Received non-all-whitespace CHARACTERS or CDATA event in nextTag()")));
252     }
253 
254     @Test
255     void testReadParentAndChildWithRegularVersionSetParentFile() throws Exception {
256         List<File> toRead = new ArrayList<>(2);
257         File parentPom = getProject("MNG-6723");
258         toRead.add(parentPom);
259         toRead.add(new File(parentPom.getParentFile(), "child/pom.xml"));
260         MavenSession mavenSession = createMavenSession(null);
261         ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
262         configuration.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
263         configuration.setRepositorySession(mavenSession.getRepositorySession());
264         org.apache.maven.project.ProjectBuilder projectBuilder =
265                 getContainer().lookup(org.apache.maven.project.ProjectBuilder.class);
266 
267         // read poms separately
268         boolean parentFileWasFoundOnChild = false;
269         for (File file : toRead) {
270             List<ProjectBuildingResult> results =
271                     projectBuilder.build(Collections.singletonList(file), false, configuration);
272             assertResultShowNoError(results);
273             MavenProject project = findChildProject(results);
274             if (project != null) {
275                 assertEquals(parentPom, project.getParentFile());
276                 parentFileWasFoundOnChild = true;
277             }
278         }
279         assertTrue(parentFileWasFoundOnChild);
280 
281         // read projects together
282         List<ProjectBuildingResult> results = projectBuilder.build(toRead, false, configuration);
283         assertResultShowNoError(results);
284         assertEquals(parentPom, findChildProject(results).getParentFile());
285         Collections.reverse(toRead);
286         results = projectBuilder.build(toRead, false, configuration);
287         assertResultShowNoError(results);
288         assertEquals(parentPom, findChildProject(results).getParentFile());
289     }
290 
291     private MavenProject findChildProject(List<ProjectBuildingResult> results) {
292         for (ProjectBuildingResult result : results) {
293             if (result.getPomFile().getParentFile().getName().equals("child")) {
294                 return result.getProject();
295             }
296         }
297         return null;
298     }
299 
300     private void assertResultShowNoError(List<ProjectBuildingResult> results) {
301         for (ProjectBuildingResult result : results) {
302             assertThat(result.getProblems(), is(empty()));
303             assertNotNull(result.getProject());
304         }
305     }
306 
307     @Test
308     void testBuildProperties() throws Exception {
309         File file = new File(getProject("MNG-6716").getParentFile(), "project/pom.xml");
310         MavenSession mavenSession = createMavenSession(null);
311         ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
312         configuration.setRepositorySession(mavenSession.getRepositorySession());
313         configuration.setResolveDependencies(true);
314         List<ProjectBuildingResult> result = projectBuilder.build(Collections.singletonList(file), true, configuration);
315         MavenProject project = result.get(0).getProject();
316         // verify a few typical parameters are not duplicated
317         assertEquals(1, project.getTestCompileSourceRoots().size());
318         assertEquals(1, project.getCompileSourceRoots().size());
319         assertEquals(1, project.getMailingLists().size());
320         assertEquals(1, project.getResources().size());
321     }
322 
323     @Test
324     void testPropertyInPluginManagementGroupId() throws Exception {
325         File pom = getProject("MNG-6983");
326 
327         MavenSession session = createMavenSession(pom);
328         MavenProject project = session.getCurrentProject();
329 
330         for (Plugin buildPlugin : project.getBuildPlugins()) {
331             assertNotNull(buildPlugin.getVersion(), "Missing version for build plugin " + buildPlugin.getKey());
332         }
333     }
334 
335     @Test
336     void testBuildFromModelSourceResolvesBasedir() throws Exception {
337         File pomFile = new File("src/test/resources/projects/modelsourcebasedir/pom.xml");
338         MavenSession mavenSession = createMavenSession(null);
339         ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
340         configuration.setRepositorySession(mavenSession.getRepositorySession());
341         ModelSource modelSource = new FileModelSource(pomFile);
342         ProjectBuildingResult result = getContainer()
343                 .lookup(org.apache.maven.project.ProjectBuilder.class)
344                 .build(modelSource, configuration);
345 
346         assertEquals(
347                 pomFile.getAbsoluteFile(),
348                 result.getProject().getModel().getPomFile().getAbsoluteFile());
349         int errors = 0;
350         for (ModelProblem p : result.getProblems()) {
351             if (p.getSeverity() == ModelProblem.Severity.ERROR) {
352                 errors++;
353             }
354         }
355         assertEquals(0, errors);
356     }
357 
358     @Test
359     void testLocationTrackingResolution() throws Exception {
360         File pom = getProject("MNG-7648");
361 
362         MavenSession session = createMavenSession(pom);
363         MavenProject project = session.getCurrentProject();
364 
365         InputLocation dependencyLocation = null;
366         for (Dependency dependency : project.getDependencies()) {
367             if (dependency.getManagementKey().equals("org.apache.maven.its:a:jar")) {
368                 dependencyLocation = dependency.getLocation("version");
369             }
370         }
371         assertNotNull(dependencyLocation, "missing dependency");
372         assertEquals(
373                 "org.apache.maven.its:bom:0.1", dependencyLocation.getSource().getModelId());
374 
375         InputLocation pluginLocation = null;
376         for (Plugin plugin : project.getBuildPlugins()) {
377             if (plugin.getKey().equals("org.apache.maven.plugins:maven-clean-plugin")) {
378                 pluginLocation = plugin.getLocation("version");
379             }
380         }
381         assertNotNull(pluginLocation, "missing build plugin");
382         assertEquals(
383                 "org.apache.maven.its:parent:0.1", pluginLocation.getSource().getModelId());
384     }
385 }