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.model.building;
20  
21  import java.io.File;
22  import java.util.concurrent.atomic.AtomicReference;
23  
24  import org.apache.maven.api.model.Dependency;
25  import org.apache.maven.api.model.Parent;
26  import org.apache.maven.api.model.Repository;
27  import org.apache.maven.model.Model;
28  import org.apache.maven.model.resolution.InvalidRepositoryException;
29  import org.apache.maven.model.resolution.ModelResolver;
30  import org.apache.maven.model.resolution.UnresolvableModelException;
31  import org.junit.jupiter.api.Test;
32  
33  import static org.junit.jupiter.api.Assertions.assertEquals;
34  import static org.junit.jupiter.api.Assertions.assertNotNull;
35  import static org.junit.jupiter.api.Assertions.assertThrows;
36  import static org.junit.jupiter.api.Assertions.assertTrue;
37  
38  /**
39   */
40  class DefaultModelBuilderTest {
41  
42      private static final String BASE1_ID = "thegroup:base1:pom";
43  
44      private static final String BASE1 = "<project>\n" + "  <modelVersion>4.0.0</modelVersion>\n"
45              + "  <groupId>thegroup</groupId>\n"
46              + "  <artifactId>base1</artifactId>\n"
47              + "  <version>1</version>\n"
48              + "  <packaging>pom</packaging>\n"
49              + "  <dependencyManagement>\n"
50              + "    <dependencies>\n"
51              + "      <dependency>\n"
52              + "        <groupId>thegroup</groupId>\n"
53              + "        <artifactId>base2</artifactId>\n"
54              + "        <version>1</version>\n"
55              + "        <type>pom</type>\n"
56              + "        <scope>import</scope>\n"
57              + "      </dependency>\n"
58              + "    </dependencies>\n"
59              + "  </dependencyManagement>\n"
60              + "</project>\n";
61  
62      private static final String BASE2_ID = "thegroup:base2:pom";
63  
64      private static final String BASE2 = "<project>\n" + "  <modelVersion>4.0.0</modelVersion>\n"
65              + "  <groupId>thegroup</groupId>\n"
66              + "  <artifactId>base2</artifactId>\n"
67              + "  <version>1</version>\n"
68              + "  <packaging>pom</packaging>\n"
69              + "  <dependencyManagement>\n"
70              + "    <dependencies>\n"
71              + "      <dependency>\n"
72              + "        <groupId>thegroup</groupId>\n"
73              + "        <artifactId>base1</artifactId>\n"
74              + "        <version>1</version>\n"
75              + "        <type>pom</type>\n"
76              + "        <scope>import</scope>\n"
77              + "      </dependency>\n"
78              + "    </dependencies>\n"
79              + "  </dependencyManagement>\n"
80              + "</project>\n";
81  
82      @Test
83      void testCycleInImports() throws Exception {
84          ModelBuilder builder = new DefaultModelBuilderFactory().newInstance();
85          assertNotNull(builder);
86  
87          DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
88          request.setModelSource(new StringModelSource(BASE1));
89          request.setModelResolver(new CycleInImportsResolver());
90  
91          assertThrows(ModelBuildingException.class, () -> builder.build(request));
92      }
93  
94      static class CycleInImportsResolver extends BaseModelResolver {
95          @Override
96          public ModelSource resolveModel(org.apache.maven.model.Dependency dependency)
97                  throws UnresolvableModelException {
98              switch (dependency.getManagementKey()) {
99                  case BASE1_ID:
100                     return new StringModelSource(BASE1);
101                 case BASE2_ID:
102                     return new StringModelSource(BASE2);
103             }
104             return null;
105         }
106     }
107 
108     static class BaseModelResolver implements ModelResolver {
109         @Override
110         public ModelSource resolveModel(String groupId, String artifactId, String version)
111                 throws UnresolvableModelException {
112             return null;
113         }
114 
115         @Override
116         public ModelSource resolveModel(Parent parent, AtomicReference<Parent> modified)
117                 throws UnresolvableModelException {
118             return null;
119         }
120 
121         @Override
122         public ModelSource resolveModel(Dependency dependency, AtomicReference<Dependency> modified)
123                 throws UnresolvableModelException {
124             return null;
125         }
126 
127         @Override
128         public void addRepository(Repository repository) throws InvalidRepositoryException {}
129 
130         @Override
131         public void addRepository(Repository repository, boolean replace) throws InvalidRepositoryException {}
132 
133         @Override
134         public ModelResolver newCopy() {
135             return this;
136         }
137 
138         @Override
139         public ModelSource resolveModel(org.apache.maven.model.Parent parent) throws UnresolvableModelException {
140             return null;
141         }
142 
143         @Override
144         public ModelSource resolveModel(org.apache.maven.model.Dependency dependency)
145                 throws UnresolvableModelException {
146             return null;
147         }
148 
149         @Override
150         public void addRepository(org.apache.maven.model.Repository repository) throws InvalidRepositoryException {}
151 
152         @Override
153         public void addRepository(org.apache.maven.model.Repository repository, boolean replace)
154                 throws InvalidRepositoryException {}
155     }
156 
157     @Test
158     void testBuildRawModel() throws Exception {
159         ModelBuilder builder = new DefaultModelBuilderFactory().newInstance();
160         assertNotNull(builder);
161 
162         Result<? extends Model> res = builder.buildRawModel(
163                 new File(getClass().getResource("/poms/factory/simple.xml").getFile()),
164                 ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL,
165                 false);
166         assertNotNull(res.get());
167     }
168 
169     /**
170      * This test has
171      *   - a dependency directly managed to 0.2
172      *   - then a BOM import which manages that same dep to 0.1
173      * This <i>currently</i> results in 0.2 and a no warning
174      */
175     @Test
176     void testManagedDependencyBeforeImport() throws Exception {
177         ModelBuilder builder = new DefaultModelBuilderFactory().newInstance();
178         assertNotNull(builder);
179 
180         DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
181         request.setModelSource(new FileModelSource(new File(
182                 getClass().getResource("/poms/depmgmt/root-dep-first.xml").getFile())));
183         request.setModelResolver(new BaseModelResolver() {
184             public ModelSource resolveModel(org.apache.maven.model.Dependency dependency)
185                     throws UnresolvableModelException {
186                 switch (dependency.getManagementKey()) {
187                     case "test:import:pom":
188                         return new FileModelSource(new File(getClass()
189                                 .getResource("/poms/depmgmt/import.xml")
190                                 .getFile()));
191                     default:
192                         throw new UnresolvableModelException(
193                                 "Cannot resolve",
194                                 dependency.getGroupId(),
195                                 dependency.getArtifactId(),
196                                 dependency.getVersion());
197                 }
198             }
199         });
200 
201         ModelBuildingResult result = builder.build(request);
202         Dependency dep = result.getEffectiveModel().getDelegate().getDependencyManagement().getDependencies().stream()
203                 .filter(d -> "test:mydep:jar".equals(d.getManagementKey()))
204                 .findFirst()
205                 .get();
206         assertEquals("0.2", dep.getVersion());
207         assertEquals(0, result.getProblems().size());
208     }
209 
210     /**
211      * This test has
212      *   - a BOM import which manages the dep to 0.1
213      *   - then a directly managed dependency to 0.2
214      * This <i>currently</i> results in 0.2 and no warning
215      */
216     @Test
217     void testManagedDependencyAfterImport() throws Exception {
218         ModelBuilder builder = new DefaultModelBuilderFactory().newInstance();
219         assertNotNull(builder);
220 
221         DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
222         request.setModelSource(new FileModelSource(new File(
223                 getClass().getResource("/poms/depmgmt/root-dep-last.xml").getFile())));
224         request.setModelResolver(new BaseModelResolver() {
225             public ModelSource resolveModel(org.apache.maven.model.Dependency dependency)
226                     throws UnresolvableModelException {
227                 switch (dependency.getManagementKey()) {
228                     case "test:import:pom":
229                         return new FileModelSource(new File(getClass()
230                                 .getResource("/poms/depmgmt/import.xml")
231                                 .getFile()));
232                     default:
233                         throw new UnresolvableModelException(
234                                 "Cannot resolve",
235                                 dependency.getGroupId(),
236                                 dependency.getArtifactId(),
237                                 dependency.getVersion());
238                 }
239             }
240         });
241 
242         ModelBuildingResult result = builder.build(request);
243         Dependency dep = result.getEffectiveModel().getDelegate().getDependencyManagement().getDependencies().stream()
244                 .filter(d -> "test:mydep:jar".equals(d.getManagementKey()))
245                 .findFirst()
246                 .get();
247         assertEquals("0.2", dep.getVersion());
248         assertEquals(0, result.getProblems().size());
249     }
250 
251     /**
252      * This test has
253      *   - a BOM import which manages dep to 0.3
254      *   - another BOM import which manages the dep to 0.1
255      * This <i>currently</i> results in 0.3 (first wins) and a warning
256      */
257     @Test
258     void testManagedDependencyTwoImports() throws Exception {
259         ModelBuilder builder = new DefaultModelBuilderFactory().newInstance();
260         assertNotNull(builder);
261 
262         DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
263         request.setModelSource(new FileModelSource(new File(
264                 getClass().getResource("/poms/depmgmt/root-two-imports.xml").getFile())));
265         request.setModelResolver(new BaseModelResolver() {
266             public ModelSource resolveModel(org.apache.maven.model.Dependency dependency)
267                     throws UnresolvableModelException {
268                 switch (dependency.getManagementKey()) {
269                     case "test:import:pom":
270                         return new FileModelSource(new File(getClass()
271                                 .getResource("/poms/depmgmt/import.xml")
272                                 .getFile()));
273                     case "test:other:pom":
274                         return new FileModelSource(new File(getClass()
275                                 .getResource("/poms/depmgmt/other-import.xml")
276                                 .getFile()));
277                     default:
278                         throw new UnresolvableModelException(
279                                 "Cannot resolve",
280                                 dependency.getGroupId(),
281                                 dependency.getArtifactId(),
282                                 dependency.getVersion());
283                 }
284             }
285         });
286 
287         ModelBuildingResult result = builder.build(request);
288         Dependency dep = result.getEffectiveModel().getDelegate().getDependencyManagement().getDependencies().stream()
289                 .filter(d -> "test:mydep:jar".equals(d.getManagementKey()))
290                 .findFirst()
291                 .get();
292         assertEquals("0.3", dep.getVersion());
293         assertEquals(1, result.getProblems().size());
294         ModelProblem problem = result.getProblems().get(0);
295         assertTrue(problem.toString().contains("Ignored POM import"));
296     }
297 
298     /**
299      * This test has
300      *   - a BOM import which itself imports the junit BOM which manages the dep to 0.1
301      *   - a BOM import to the junit BOM which manages the dep to 0.2
302      * This <i>currently</i> results in 0.1 (first wins, unexpected as this is not the closest) and a warning
303      */
304     @Test
305     void testManagedDependencyDistance() throws Exception {
306         ModelBuilder builder = new DefaultModelBuilderFactory().newInstance();
307         assertNotNull(builder);
308 
309         DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
310         request.setLocationTracking(true);
311         request.setModelSource(new FileModelSource(new File(
312                 getClass().getResource("/poms/depmgmt/root-distance.xml").getFile())));
313         request.setModelResolver(new BaseModelResolver() {
314             public ModelSource resolveModel(org.apache.maven.model.Dependency dependency)
315                     throws UnresolvableModelException {
316                 switch (dependency.getManagementKey()) {
317                     case "org.junit:bom:pom":
318                         return new FileModelSource(new File(getClass()
319                                 .getResource("/poms/depmgmt/junit-" + dependency.getVersion() + ".xml")
320                                 .getFile()));
321                     case "test:other:pom":
322                         return new FileModelSource(new File(getClass()
323                                 .getResource("/poms/depmgmt/distant-import.xml")
324                                 .getFile()));
325                     default:
326                         throw new UnresolvableModelException(
327                                 "Cannot resolve",
328                                 dependency.getGroupId(),
329                                 dependency.getArtifactId(),
330                                 dependency.getVersion());
331                 }
332             }
333         });
334 
335         ModelBuildingResult result = builder.build(request);
336         Dependency dep = result.getEffectiveModel().getDelegate().getDependencyManagement().getDependencies().stream()
337                 .filter(d -> "org.junit:junit:jar".equals(d.getManagementKey()))
338                 .findFirst()
339                 .get();
340         assertEquals("0.1", dep.getVersion());
341         assertEquals(1, result.getProblems().size());
342         ModelProblem problem = result.getProblems().get(0);
343         assertTrue(problem.toString().contains("Ignored POM import"));
344     }
345 
346     /**
347      * This test has
348      *   - a BOM import which itself imports the junit BOM which manages the dep to 0.1
349      *   - a BOM import to the junit BOM which manages the dep to 0.2
350      *   - a direct managed dependency to the dep at 0.3 (as suggested by the warning)
351      * This results in 0.3 (explicit managed wins) and no warning
352      */
353     @Test
354     void testManagedDependencyDistanceWithExplicit() throws Exception {
355         ModelBuilder builder = new DefaultModelBuilderFactory().newInstance();
356         assertNotNull(builder);
357 
358         DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
359         request.setLocationTracking(true);
360         request.setModelSource(new FileModelSource(new File(getClass()
361                 .getResource("/poms/depmgmt/root-distance-explicit.xml")
362                 .getFile())));
363         request.setModelResolver(new BaseModelResolver() {
364             public ModelSource resolveModel(org.apache.maven.model.Dependency dependency)
365                     throws UnresolvableModelException {
366                 switch (dependency.getManagementKey()) {
367                     case "org.junit:bom:pom":
368                         return new FileModelSource(new File(getClass()
369                                 .getResource("/poms/depmgmt/junit-" + dependency.getVersion() + ".xml")
370                                 .getFile()));
371                     case "test:other:pom":
372                         return new FileModelSource(new File(getClass()
373                                 .getResource("/poms/depmgmt/distant-import.xml")
374                                 .getFile()));
375                     default:
376                         throw new UnresolvableModelException(
377                                 "Cannot resolve",
378                                 dependency.getGroupId(),
379                                 dependency.getArtifactId(),
380                                 dependency.getVersion());
381                 }
382             }
383         });
384 
385         ModelBuildingResult result = builder.build(request);
386         Dependency dep = result.getEffectiveModel().getDelegate().getDependencyManagement().getDependencies().stream()
387                 .filter(d -> "org.junit:junit:jar".equals(d.getManagementKey()))
388                 .findFirst()
389                 .get();
390         assertEquals("0.3", dep.getVersion());
391         assertEquals(0, result.getProblems().size());
392     }
393 }