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.interpolation;
20  
21  import java.io.File;
22  import java.text.SimpleDateFormat;
23  import java.util.Calendar;
24  import java.util.Date;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Properties;
28  import java.util.TimeZone;
29  
30  import org.apache.maven.model.Build;
31  import org.apache.maven.model.Dependency;
32  import org.apache.maven.model.Model;
33  import org.apache.maven.model.Organization;
34  import org.apache.maven.model.Plugin;
35  import org.apache.maven.model.Repository;
36  import org.apache.maven.model.Resource;
37  import org.apache.maven.model.Scm;
38  import org.apache.maven.model.building.DefaultModelBuildingRequest;
39  import org.apache.maven.model.building.ModelBuildingRequest;
40  import org.apache.maven.model.building.SimpleProblemCollector;
41  import org.apache.maven.model.path.PathTranslator;
42  import org.codehaus.plexus.util.xml.Xpp3Dom;
43  import org.junit.jupiter.api.Test;
44  
45  import static org.junit.jupiter.api.Assertions.assertEquals;
46  import static org.junit.jupiter.api.Assertions.assertNotNull;
47  
48  /**
49   * @author jdcasey
50   */
51  public abstract class AbstractModelInterpolatorTest {
52      private Properties context;
53  
54      protected void setUp() throws Exception {
55          context = new Properties();
56          context.put("basedir", "myBasedir");
57          context.put("project.baseUri", "myBaseUri");
58      }
59  
60      protected void assertProblemFree(SimpleProblemCollector collector) {
61          assertEquals(0, collector.getErrors().size(), "Expected no errors");
62          assertEquals(0, collector.getWarnings().size(), "Expected no warnings");
63          assertEquals(0, collector.getFatals().size(), "Expected no fatals");
64      }
65  
66      /**
67       * @deprecated instead use {@link #assertCollectorState(int, int, int, SimpleProblemCollector)}
68       */
69      @Deprecated
70      protected void assertColllectorState(
71              int numFatals, int numErrors, int numWarnings, SimpleProblemCollector collector) {
72          assertEquals(numErrors, collector.getErrors().size(), "Errors");
73          assertEquals(numWarnings, collector.getWarnings().size(), "Warnings");
74          assertEquals(numFatals, collector.getFatals().size(), "Fatals");
75      }
76  
77      protected void assertCollectorState(
78              int numFatals, int numErrors, int numWarnings, SimpleProblemCollector collector) {
79          assertColllectorState(numFatals, numErrors, numWarnings, collector);
80      }
81  
82      private ModelBuildingRequest createModelBuildingRequest(Properties p) {
83          ModelBuildingRequest config = new DefaultModelBuildingRequest();
84          if (p != null) {
85              config.setSystemProperties(p);
86          }
87          return config;
88      }
89  
90      @Test
91      public void testDefaultBuildTimestampFormatShouldFormatTimeIn24HourFormat() {
92          Calendar cal = Calendar.getInstance();
93          cal.setTimeZone(MavenBuildTimestamp.DEFAULT_BUILD_TIME_ZONE);
94          cal.set(Calendar.HOUR, 12);
95          cal.set(Calendar.AM_PM, Calendar.AM);
96  
97          // just to make sure all the bases are covered...
98          cal.set(Calendar.HOUR_OF_DAY, 0);
99          cal.set(Calendar.MINUTE, 16);
100         cal.set(Calendar.SECOND, 0);
101         cal.set(Calendar.YEAR, 1976);
102         cal.set(Calendar.MONTH, Calendar.NOVEMBER);
103         cal.set(Calendar.DATE, 11);
104 
105         Date firstTestDate = cal.getTime();
106 
107         cal.set(Calendar.HOUR, 11);
108         cal.set(Calendar.AM_PM, Calendar.PM);
109 
110         // just to make sure all the bases are covered...
111         cal.set(Calendar.HOUR_OF_DAY, 23);
112 
113         Date secondTestDate = cal.getTime();
114 
115         SimpleDateFormat format = new SimpleDateFormat(MavenBuildTimestamp.DEFAULT_BUILD_TIMESTAMP_FORMAT);
116         format.setTimeZone(MavenBuildTimestamp.DEFAULT_BUILD_TIME_ZONE);
117         assertEquals("1976-11-11T00:16:00Z", format.format(firstTestDate));
118         assertEquals("1976-11-11T23:16:00Z", format.format(secondTestDate));
119     }
120 
121     @Test
122     public void testDefaultBuildTimestampFormatWithLocalTimeZoneMidnightRollover() {
123         Calendar cal = Calendar.getInstance();
124         cal.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
125 
126         cal.set(Calendar.HOUR_OF_DAY, 1);
127         cal.set(Calendar.MINUTE, 16);
128         cal.set(Calendar.SECOND, 0);
129         cal.set(Calendar.YEAR, 2014);
130         cal.set(Calendar.MONTH, Calendar.JUNE);
131         cal.set(Calendar.DATE, 16);
132 
133         Date firstTestDate = cal.getTime();
134 
135         cal.set(Calendar.MONTH, Calendar.NOVEMBER);
136 
137         Date secondTestDate = cal.getTime();
138 
139         SimpleDateFormat format = new SimpleDateFormat(MavenBuildTimestamp.DEFAULT_BUILD_TIMESTAMP_FORMAT);
140         format.setTimeZone(MavenBuildTimestamp.DEFAULT_BUILD_TIME_ZONE);
141         assertEquals("2014-06-15T23:16:00Z", format.format(firstTestDate));
142         assertEquals("2014-11-16T00:16:00Z", format.format(secondTestDate));
143     }
144 
145     @Test
146     public void testShouldNotThrowExceptionOnReferenceToNonExistentValue() throws Exception {
147         Model model = new Model();
148 
149         Scm scm = new Scm();
150         scm.setConnection("${test}/somepath");
151 
152         model.setScm(scm);
153 
154         ModelInterpolator interpolator = createInterpolator();
155 
156         final SimpleProblemCollector collector = new SimpleProblemCollector();
157         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
158 
159         assertProblemFree(collector);
160         assertEquals("${test}/somepath", out.getScm().getConnection());
161     }
162 
163     @Test
164     public void testShouldThrowExceptionOnRecursiveScmConnectionReference() throws Exception {
165         Model model = new Model();
166 
167         Scm scm = new Scm();
168         scm.setConnection("${project.scm.connection}/somepath");
169 
170         model.setScm(scm);
171 
172         try {
173             ModelInterpolator interpolator = createInterpolator();
174 
175             final SimpleProblemCollector collector = new SimpleProblemCollector();
176             interpolator.interpolateModel(model, null, createModelBuildingRequest(context), collector);
177             assertCollectorState(0, 1, 0, collector);
178         } catch (Exception e) {
179 
180         }
181     }
182 
183     @Test
184     public void testShouldNotThrowExceptionOnReferenceToValueContainingNakedExpression() throws Exception {
185         Model model = new Model();
186 
187         Scm scm = new Scm();
188         scm.setConnection("${test}/somepath");
189 
190         model.setScm(scm);
191 
192         model.addProperty("test", "test");
193 
194         ModelInterpolator interpolator = createInterpolator();
195 
196         final SimpleProblemCollector collector = new SimpleProblemCollector();
197         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
198 
199         assertProblemFree(collector);
200 
201         assertEquals("test/somepath", out.getScm().getConnection());
202     }
203 
204     @Test
205     public void testShouldInterpolateOrganizationNameCorrectly() throws Exception {
206         String orgName = "MyCo";
207 
208         Model model = new Model();
209         model.setName("${pom.organization.name} Tools");
210 
211         Organization org = new Organization();
212         org.setName(orgName);
213 
214         model.setOrganization(org);
215 
216         ModelInterpolator interpolator = createInterpolator();
217 
218         Model out = interpolator.interpolateModel(
219                 model, new File("."), createModelBuildingRequest(context), new SimpleProblemCollector());
220 
221         assertEquals(orgName + " Tools", out.getName());
222     }
223 
224     @Test
225     public void testShouldInterpolateDependencyVersionToSetSameAsProjectVersion() throws Exception {
226         Model model = new Model();
227         model.setVersion("3.8.1");
228 
229         Dependency dep = new Dependency();
230         dep.setVersion("${version}");
231 
232         model.addDependency(dep);
233 
234         ModelInterpolator interpolator = createInterpolator();
235 
236         final SimpleProblemCollector collector = new SimpleProblemCollector();
237         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
238         assertCollectorState(0, 0, 1, collector);
239 
240         assertEquals("3.8.1", (out.getDependencies().get(0)).getVersion());
241     }
242 
243     @Test
244     public void testShouldNotInterpolateDependencyVersionWithInvalidReference() throws Exception {
245         Model model = new Model();
246         model.setVersion("3.8.1");
247 
248         Dependency dep = new Dependency();
249         dep.setVersion("${something}");
250 
251         model.addDependency(dep);
252 
253         /*
254         // This is the desired behaviour, however there are too many crappy poms in the repo and an issue with the
255         // timing of executing the interpolation
256 
257         try
258         {
259         new RegexBasedModelInterpolator().interpolate( model, context );
260         fail( "Should have failed to interpolate with invalid reference" );
261         }
262         catch ( ModelInterpolationException expected )
263         {
264         assertTrue( true );
265         }
266         */
267 
268         ModelInterpolator interpolator = createInterpolator();
269 
270         final SimpleProblemCollector collector = new SimpleProblemCollector();
271         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
272         assertProblemFree(collector);
273 
274         assertEquals("${something}", (out.getDependencies().get(0)).getVersion());
275     }
276 
277     @Test
278     public void testTwoReferences() throws Exception {
279         Model model = new Model();
280         model.setVersion("3.8.1");
281         model.setArtifactId("foo");
282 
283         Dependency dep = new Dependency();
284         dep.setVersion("${artifactId}-${version}");
285 
286         model.addDependency(dep);
287 
288         ModelInterpolator interpolator = createInterpolator();
289 
290         final SimpleProblemCollector collector = new SimpleProblemCollector();
291         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
292         assertCollectorState(0, 0, 2, collector);
293 
294         assertEquals("foo-3.8.1", (out.getDependencies().get(0)).getVersion());
295     }
296 
297     @Test
298     public void testBasedir() throws Exception {
299         Model model = new Model();
300         model.setVersion("3.8.1");
301         model.setArtifactId("foo");
302 
303         Repository repository = new Repository();
304 
305         repository.setUrl("file://localhost/${basedir}/temp-repo");
306 
307         model.addRepository(repository);
308 
309         ModelInterpolator interpolator = createInterpolator();
310 
311         final SimpleProblemCollector collector = new SimpleProblemCollector();
312         Model out = interpolator.interpolateModel(model, null, createModelBuildingRequest(context), collector);
313         assertProblemFree(collector);
314 
315         assertEquals(
316                 "file://localhost/myBasedir/temp-repo", (out.getRepositories().get(0)).getUrl());
317     }
318 
319     @Test
320     public void testBaseUri() throws Exception {
321         Model model = new Model();
322         model.setVersion("3.8.1");
323         model.setArtifactId("foo");
324 
325         Repository repository = new Repository();
326 
327         repository.setUrl("${project.baseUri}/temp-repo");
328 
329         model.addRepository(repository);
330 
331         ModelInterpolator interpolator = createInterpolator();
332 
333         final SimpleProblemCollector collector = new SimpleProblemCollector();
334         Model out = interpolator.interpolateModel(model, null, createModelBuildingRequest(context), collector);
335         assertProblemFree(collector);
336 
337         assertEquals("myBaseUri/temp-repo", (out.getRepositories().get(0)).getUrl());
338     }
339 
340     @Test
341     public void testEnvars() throws Exception {
342         Properties context = new Properties();
343 
344         context.put("env.HOME", "/path/to/home");
345 
346         Model model = new Model();
347 
348         Properties modelProperties = new Properties();
349 
350         modelProperties.setProperty("outputDirectory", "${env.HOME}");
351 
352         model.setProperties(modelProperties);
353 
354         ModelInterpolator interpolator = createInterpolator();
355 
356         final SimpleProblemCollector collector = new SimpleProblemCollector();
357         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
358         assertProblemFree(collector);
359 
360         assertEquals("/path/to/home", out.getProperties().getProperty("outputDirectory"));
361     }
362 
363     @Test
364     public void testMinimalValidationInterpolationUsesRestrictedPropertySet() throws Exception {
365         Properties context = new Properties();
366         context.put("env.SOME_VAR", "some-value");
367         context.put("some.property", "other-value");
368         context.put("java.version", "21");
369 
370         Model model = new Model();
371 
372         Properties modelProperties = new Properties();
373         modelProperties.setProperty("envDir", "${env.SOME_VAR}");
374         modelProperties.setProperty("propDir", "${some.property}");
375         modelProperties.setProperty("jdk", "${java.version}");
376 
377         model.setProperties(modelProperties);
378 
379         ModelInterpolator interpolator = createInterpolator();
380 
381         final SimpleProblemCollector collector = new SimpleProblemCollector();
382         ModelBuildingRequest config = createModelBuildingRequest(context);
383         config.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
384         Model out = interpolator.interpolateModel(model, new File("."), config, collector);
385         assertProblemFree(collector);
386 
387         // At minimal validation level (the level used for models built while resolving
388         // dependency, parent and BOM-import POMs) env and arbitrary system/user properties
389         // stay literal...
390         assertEquals("${env.SOME_VAR}", out.getProperties().getProperty("envDir"));
391         assertEquals("${some.property}", out.getProperties().getProperty("propDir"));
392         // ...while JVM-defined and other well-known expressions keep resolving.
393         assertEquals("21", out.getProperties().getProperty("jdk"));
394     }
395 
396     @Test
397     public void testFullInterpolationOptOutRestoresPreviousBehaviorAtMinimalValidationLevel() throws Exception {
398         Properties context = new Properties();
399         context.put("env.SOME_VAR", "some-value");
400         context.put(AbstractStringBasedModelInterpolator.FULL_EXTERNAL_INTERPOLATION_PROPERTY, "true");
401 
402         Model model = new Model();
403 
404         Properties modelProperties = new Properties();
405         modelProperties.setProperty("envDir", "${env.SOME_VAR}");
406 
407         model.setProperties(modelProperties);
408 
409         ModelInterpolator interpolator = createInterpolator();
410 
411         final SimpleProblemCollector collector = new SimpleProblemCollector();
412         ModelBuildingRequest config = createModelBuildingRequest(context);
413         config.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
414         Model out = interpolator.interpolateModel(model, new File("."), config, collector);
415         assertProblemFree(collector);
416 
417         assertEquals("some-value", out.getProperties().getProperty("envDir"));
418     }
419 
420     @Test
421     public void testEnvarsStillInterpolatedAtStrictValidationLevel() throws Exception {
422         Properties context = new Properties();
423         context.put("env.SOME_VAR", "some-value");
424         context.put("some.property", "other-value");
425 
426         Model model = new Model();
427 
428         Properties modelProperties = new Properties();
429         modelProperties.setProperty("envDir", "${env.SOME_VAR}");
430         modelProperties.setProperty("propDir", "${some.property}");
431 
432         model.setProperties(modelProperties);
433 
434         ModelInterpolator interpolator = createInterpolator();
435 
436         final SimpleProblemCollector collector = new SimpleProblemCollector();
437         ModelBuildingRequest config = createModelBuildingRequest(context);
438         config.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_STRICT);
439         Model out = interpolator.interpolateModel(model, new File("."), config, collector);
440         assertProblemFree(collector);
441 
442         // Operator project builds use strict validation and keep full interpolation,
443         // unchanged from previous behavior.
444         assertEquals("some-value", out.getProperties().getProperty("envDir"));
445         assertEquals("other-value", out.getProperties().getProperty("propDir"));
446     }
447 
448     @Test
449     public void testEnvarExpressionThatEvaluatesToNullReturnsTheLiteralString() throws Exception {
450         Model model = new Model();
451 
452         Properties modelProperties = new Properties();
453 
454         modelProperties.setProperty("outputDirectory", "${env.DOES_NOT_EXIST}");
455 
456         model.setProperties(modelProperties);
457 
458         ModelInterpolator interpolator = createInterpolator();
459 
460         final SimpleProblemCollector collector = new SimpleProblemCollector();
461         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
462         assertProblemFree(collector);
463 
464         assertEquals(out.getProperties().getProperty("outputDirectory"), "${env.DOES_NOT_EXIST}");
465     }
466 
467     @Test
468     public void testExpressionThatEvaluatesToNullReturnsTheLiteralString() throws Exception {
469         Model model = new Model();
470 
471         Properties modelProperties = new Properties();
472 
473         modelProperties.setProperty("outputDirectory", "${DOES_NOT_EXIST}");
474 
475         model.setProperties(modelProperties);
476 
477         ModelInterpolator interpolator = createInterpolator();
478 
479         final SimpleProblemCollector collector = new SimpleProblemCollector();
480         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
481         assertProblemFree(collector);
482 
483         assertEquals(out.getProperties().getProperty("outputDirectory"), "${DOES_NOT_EXIST}");
484     }
485 
486     @Test
487     public void testShouldInterpolateSourceDirectoryReferencedFromResourceDirectoryCorrectly() throws Exception {
488         Model model = new Model();
489 
490         Build build = new Build();
491         build.setSourceDirectory("correct");
492 
493         Resource res = new Resource();
494         res.setDirectory("${project.build.sourceDirectory}");
495 
496         build.addResource(res);
497 
498         Resource res2 = new Resource();
499         res2.setDirectory("${pom.build.sourceDirectory}");
500 
501         build.addResource(res2);
502 
503         Resource res3 = new Resource();
504         res3.setDirectory("${build.sourceDirectory}");
505 
506         build.addResource(res3);
507 
508         model.setBuild(build);
509 
510         ModelInterpolator interpolator = createInterpolator();
511 
512         final SimpleProblemCollector collector = new SimpleProblemCollector();
513         Model out = interpolator.interpolateModel(model, null, createModelBuildingRequest(context), collector);
514         assertCollectorState(0, 0, 2, collector);
515 
516         List<Resource> outResources = out.getBuild().getResources();
517         Iterator<Resource> resIt = outResources.iterator();
518 
519         assertEquals(build.getSourceDirectory(), resIt.next().getDirectory());
520         assertEquals(build.getSourceDirectory(), resIt.next().getDirectory());
521         assertEquals(build.getSourceDirectory(), resIt.next().getDirectory());
522     }
523 
524     @Test
525     public void testShouldInterpolateUnprefixedBasedirExpression() throws Exception {
526         File basedir = new File("/test/path");
527         Model model = new Model();
528         Dependency dep = new Dependency();
529         dep.setSystemPath("${basedir}/artifact.jar");
530 
531         model.addDependency(dep);
532 
533         ModelInterpolator interpolator = createInterpolator();
534 
535         final SimpleProblemCollector collector = new SimpleProblemCollector();
536         Model result = interpolator.interpolateModel(model, basedir, createModelBuildingRequest(context), collector);
537         assertProblemFree(collector);
538 
539         List<Dependency> rDeps = result.getDependencies();
540         assertNotNull(rDeps);
541         assertEquals(1, rDeps.size());
542         assertEquals(
543                 new File(basedir, "artifact.jar").getAbsolutePath(),
544                 new File(rDeps.get(0).getSystemPath()).getAbsolutePath());
545     }
546 
547     @Test
548     public void testRecursiveExpressionCycleInPluginConfiguration() throws Exception {
549         // MNG-8174: a self-referencing property used in a plugin configuration attribute must not
550         // cause an NPE while interpolating the plugin configuration
551         Model model = new Model();
552         model.addProperty("prop", "${prop}");
553 
554         Xpp3Dom configuration = new Xpp3Dom("configuration");
555         Xpp3Dom filter = new Xpp3Dom("filter");
556         filter.setAttribute("token", "key");
557         filter.setAttribute("value", "${prop}");
558         filter.setValue("${prop}");
559         configuration.addChild(filter);
560 
561         Plugin plugin = new Plugin();
562         plugin.setArtifactId("maven-antrun-plugin");
563         plugin.setConfiguration(configuration);
564 
565         Build build = new Build();
566         build.addPlugin(plugin);
567         model.setBuild(build);
568 
569         SimpleProblemCollector collector = new SimpleProblemCollector();
570         ModelInterpolator interpolator = createInterpolator();
571         Model out = interpolator.interpolateModel(model, null, createModelBuildingRequest(context), collector);
572 
573         assertNotNull(out);
574         assertCollectorState(0, 3, 0, collector);
575         Xpp3Dom outConfig = (Xpp3Dom) out.getBuild().getPlugins().get(0).getConfiguration();
576         assertEquals("${prop}", outConfig.getChild("filter").getAttribute("value"));
577         assertEquals("${prop}", outConfig.getChild("filter").getValue());
578     }
579 
580     protected abstract ModelInterpolator createInterpolator(PathTranslator translator) throws Exception;
581 
582     protected abstract ModelInterpolator createInterpolator() throws Exception;
583 }