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.util.ArrayList;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Properties;
28  import java.util.concurrent.Callable;
29  import java.util.concurrent.CountDownLatch;
30  import java.util.concurrent.Future;
31  import java.util.concurrent.FutureTask;
32  
33  import org.apache.maven.model.InputLocation;
34  import org.apache.maven.model.InputSource;
35  import org.apache.maven.model.Model;
36  import org.apache.maven.model.building.DefaultModelBuildingRequest;
37  import org.apache.maven.model.building.ModelBuildingRequest;
38  import org.apache.maven.model.building.SimpleProblemCollector;
39  import org.junit.jupiter.api.BeforeEach;
40  import org.junit.jupiter.api.Test;
41  
42  import static org.hamcrest.CoreMatchers.anyOf;
43  import static org.hamcrest.CoreMatchers.is;
44  import static org.hamcrest.MatcherAssert.assertThat;
45  import static org.junit.jupiter.api.Assertions.assertEquals;
46  import static org.junit.jupiter.api.Assertions.assertNotNull;
47  import static org.powermock.reflect.Whitebox.getField;
48  import static org.powermock.reflect.Whitebox.getInternalState;
49  
50  /**
51   * @author jdcasey
52   * @author Benjamin Bentmann
53   */
54  public class StringSearchModelInterpolatorTest extends AbstractModelInterpolatorTest {
55  
56      protected ModelInterpolator interpolator;
57  
58      @BeforeEach
59      protected void setUp() throws Exception {
60          super.setUp();
61          interpolator =
62                  new StringSearchModelInterpolator().setVersionPropertiesProcessor(new DefaultModelVersionProcessor());
63      }
64  
65      protected ModelInterpolator createInterpolator(org.apache.maven.model.path.PathTranslator translator)
66              throws Exception {
67          return this.interpolator;
68      }
69  
70      protected ModelInterpolator createInterpolator() throws Exception {
71          return this.interpolator;
72      }
73  
74      @Test
75      public void testInterpolateStringArray() throws Exception {
76          Model model = new Model();
77  
78          Properties p = new Properties();
79          p.setProperty("key", "value");
80          p.setProperty("key2", "value2");
81  
82          String[] values = {"${key}", "${key2}"};
83  
84          StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
85  
86          ModelBuildingRequest config = createModelBuildingRequest(p);
87  
88          final SimpleProblemCollector collector = new SimpleProblemCollector();
89          interpolator.interpolateObject(values, model, new File("."), config, collector);
90          assertProblemFree(collector);
91  
92          assertEquals("value", values[0]);
93          assertEquals("value2", values[1]);
94      }
95  
96      private ModelBuildingRequest createModelBuildingRequest(Properties p) {
97          ModelBuildingRequest config = new DefaultModelBuildingRequest();
98          config.setSystemProperties(p);
99          return config;
100     }
101 
102     @Test
103     public void testInterpolateObjectWithStringArrayField() throws Exception {
104         Model model = new Model();
105 
106         Properties p = new Properties();
107         p.setProperty("key", "value");
108         p.setProperty("key2", "value2");
109 
110         String[] values = {"${key}", "${key2}"};
111 
112         ObjectWithStringArrayField obj = new ObjectWithStringArrayField(values);
113 
114         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
115 
116         ModelBuildingRequest config = createModelBuildingRequest(p);
117 
118         final SimpleProblemCollector collector = new SimpleProblemCollector();
119         interpolator.interpolateObject(obj, model, new File("."), config, collector);
120         assertProblemFree(collector);
121 
122         assertEquals("value", obj.values[0]);
123         assertEquals("value2", obj.values[1]);
124     }
125 
126     @Test
127     public void testInterpolateObjectWithStringListField() throws Exception {
128         Model model = new Model();
129 
130         Properties p = new Properties();
131         p.setProperty("key", "value");
132         p.setProperty("key2", "value2");
133 
134         List<String> values = new ArrayList<>();
135         values.add("${key}");
136         values.add("${key2}");
137 
138         ObjectWithListField obj = new ObjectWithListField(values);
139 
140         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
141 
142         ModelBuildingRequest config = createModelBuildingRequest(p);
143 
144         final SimpleProblemCollector collector = new SimpleProblemCollector();
145         interpolator.interpolateObject(obj, model, new File("."), config, collector);
146         assertProblemFree(collector);
147 
148         assertEquals("value", obj.values.get(0));
149         assertEquals("value2", obj.values.get(1));
150     }
151 
152     @Test
153     public void testInterpolateObjectWithStringListFieldAndOneLiteralValue() throws Exception {
154         Model model = new Model();
155 
156         Properties p = new Properties();
157         p.setProperty("key", "value");
158         p.setProperty("key2", "value2");
159 
160         List<String> values = new ArrayList<>();
161         values.add("key");
162         values.add("${key2}");
163 
164         ObjectWithListField obj = new ObjectWithListField(values);
165 
166         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
167 
168         ModelBuildingRequest config = createModelBuildingRequest(p);
169 
170         final SimpleProblemCollector collector = new SimpleProblemCollector();
171         interpolator.interpolateObject(obj, model, new File("."), config, collector);
172         assertProblemFree(collector);
173 
174         assertEquals("key", obj.values.get(0));
175         assertEquals("value2", obj.values.get(1));
176     }
177 
178     @Test
179     public void testInterpolateObjectWithUnmodifiableStringListField() throws Exception {
180         Model model = new Model();
181 
182         Properties p = new Properties();
183         p.setProperty("key", "value");
184         p.setProperty("key2", "value2");
185 
186         List<String> values = Collections.unmodifiableList(Collections.singletonList("${key}"));
187 
188         ObjectWithListField obj = new ObjectWithListField(values);
189 
190         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
191 
192         ModelBuildingRequest config = createModelBuildingRequest(p);
193 
194         final SimpleProblemCollector collector = new SimpleProblemCollector();
195         interpolator.interpolateObject(obj, model, new File("."), config, collector);
196         assertProblemFree(collector);
197 
198         assertEquals("${key}", obj.values.get(0));
199     }
200 
201     @Test
202     public void testInterpolateObjectWithStringArrayListField() throws Exception {
203         Model model = new Model();
204 
205         Properties p = new Properties();
206         p.setProperty("key", "value");
207         p.setProperty("key2", "value2");
208         p.setProperty("key3", "value3");
209         p.setProperty("key4", "value4");
210 
211         List<String[]> values = new ArrayList<>();
212         values.add(new String[] {"${key}", "${key2}"});
213         values.add(new String[] {"${key3}", "${key4}"});
214 
215         ObjectWithListField obj = new ObjectWithListField(values);
216 
217         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
218 
219         ModelBuildingRequest config = createModelBuildingRequest(p);
220 
221         final SimpleProblemCollector collector = new SimpleProblemCollector();
222         interpolator.interpolateObject(obj, model, new File("."), config, collector);
223         assertProblemFree(collector);
224 
225         assertEquals("value", ((String[]) obj.values.get(0))[0]);
226         assertEquals("value2", ((String[]) obj.values.get(0))[1]);
227         assertEquals("value3", ((String[]) obj.values.get(1))[0]);
228         assertEquals("value4", ((String[]) obj.values.get(1))[1]);
229     }
230 
231     @Test
232     public void testInterpolateObjectWithStringToStringMapField() throws Exception {
233         Model model = new Model();
234 
235         Properties p = new Properties();
236         p.setProperty("key", "value");
237         p.setProperty("key2", "value2");
238 
239         Map<String, String> values = new HashMap<>();
240         values.put("key", "${key}");
241         values.put("key2", "${key2}");
242 
243         ObjectWithMapField obj = new ObjectWithMapField(values);
244 
245         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
246 
247         ModelBuildingRequest config = createModelBuildingRequest(p);
248 
249         final SimpleProblemCollector collector = new SimpleProblemCollector();
250         interpolator.interpolateObject(obj, model, new File("."), config, collector);
251         assertProblemFree(collector);
252 
253         assertEquals("value", obj.values.get("key"));
254         assertEquals("value2", obj.values.get("key2"));
255     }
256 
257     @Test
258     public void testInterpolateObjectWithStringToStringMapFieldAndOneLiteralValue() throws Exception {
259         Model model = new Model();
260 
261         Properties p = new Properties();
262         p.setProperty("key", "value");
263         p.setProperty("key2", "value2");
264 
265         Map<String, String> values = new HashMap<>();
266         values.put("key", "val");
267         values.put("key2", "${key2}");
268 
269         ObjectWithMapField obj = new ObjectWithMapField(values);
270 
271         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
272 
273         ModelBuildingRequest config = createModelBuildingRequest(p);
274 
275         final SimpleProblemCollector collector = new SimpleProblemCollector();
276         interpolator.interpolateObject(obj, model, new File("."), config, collector);
277         assertProblemFree(collector);
278 
279         assertEquals("val", obj.values.get("key"));
280         assertEquals("value2", obj.values.get("key2"));
281     }
282 
283     @Test
284     public void testInterpolateObjectWithUnmodifiableStringToStringMapField() throws Exception {
285         Model model = new Model();
286 
287         Properties p = new Properties();
288         p.setProperty("key", "value");
289         p.setProperty("key2", "value2");
290 
291         Map<String, String> values = Collections.unmodifiableMap(Collections.singletonMap("key", "${key}"));
292 
293         ObjectWithMapField obj = new ObjectWithMapField(values);
294 
295         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
296 
297         ModelBuildingRequest config = createModelBuildingRequest(p);
298 
299         final SimpleProblemCollector collector = new SimpleProblemCollector();
300         interpolator.interpolateObject(obj, model, new File("."), config, collector);
301         assertProblemFree(collector);
302 
303         assertEquals("${key}", obj.values.get("key"));
304     }
305 
306     @Test
307     public void testInterpolateObjectWithStringToStringArrayMapField() throws Exception {
308         Model model = new Model();
309 
310         Properties p = new Properties();
311         p.setProperty("key", "value");
312         p.setProperty("key2", "value2");
313         p.setProperty("key3", "value3");
314         p.setProperty("key4", "value4");
315 
316         Map<String, String[]> values = new HashMap<>();
317         values.put("key", new String[] {"${key}", "${key2}"});
318         values.put("key2", new String[] {"${key3}", "${key4}"});
319 
320         ObjectWithMapField obj = new ObjectWithMapField(values);
321 
322         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
323 
324         ModelBuildingRequest config = createModelBuildingRequest(p);
325 
326         final SimpleProblemCollector collector = new SimpleProblemCollector();
327         interpolator.interpolateObject(obj, model, new File("."), config, collector);
328         assertProblemFree(collector);
329 
330         assertEquals("value", ((String[]) obj.values.get("key"))[0]);
331         assertEquals("value2", ((String[]) obj.values.get("key"))[1]);
332         assertEquals("value3", ((String[]) obj.values.get("key2"))[0]);
333         assertEquals("value4", ((String[]) obj.values.get("key2"))[1]);
334     }
335 
336     @Test
337     public void testInterpolateObjectWithPomFile() throws Exception {
338         Model model = new Model();
339         model.setPomFile(new File(System.getProperty("user.dir"), "pom.xml"));
340         File baseDir = model.getProjectDirectory();
341 
342         Properties p = new Properties();
343 
344         Map<String, String> values = new HashMap<>();
345         values.put("key", "${project.basedir}" + File.separator + "target");
346 
347         ObjectWithMapField obj = new ObjectWithMapField(values);
348 
349         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
350 
351         ModelBuildingRequest config = createModelBuildingRequest(p);
352 
353         SimpleProblemCollector collector = new SimpleProblemCollector();
354         interpolator.interpolateObject(obj, model, new File("."), config, collector);
355         assertProblemFree(collector);
356 
357         assertEquals(System.getProperty("user.dir"), baseDir.getAbsolutePath());
358         assertEquals(1, obj.values.size());
359         assertThat(
360                 (String) obj.values.get("key"),
361                 is(anyOf(
362                         is(System.getProperty("user.dir") + File.separator + "target"),
363                         // TODO why MVN adds dot /./ in paths???
364                         is(System.getProperty("user.dir") + File.separator + '.' + File.separator + "target"))));
365     }
366 
367     @Test
368     public void testNotInterpolateObjectWithFile() throws Exception {
369         Model model = new Model();
370 
371         File baseDir = new File(System.getProperty("user.dir"));
372 
373         Properties p = new Properties();
374 
375         ObjectWithNotInterpolatedFile obj = new ObjectWithNotInterpolatedFile(baseDir);
376 
377         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
378 
379         ModelBuildingRequest config = createModelBuildingRequest(p);
380 
381         SimpleProblemCollector collector = new SimpleProblemCollector();
382         interpolator.interpolateObject(obj, model, new File("."), config, collector);
383         assertProblemFree(collector);
384 
385         //noinspection unchecked
386         Map<Class<?>, ?> cache = (Map<Class<?>, ?>)
387                 getField(StringSearchModelInterpolator.class, "CACHED_ENTRIES").get(null);
388 
389         Object objCacheItem = cache.get(Object.class);
390         Object fileCacheItem = cache.get(File.class);
391 
392         assertNotNull(objCacheItem);
393         assertNotNull(fileCacheItem);
394 
395         assertEquals(0, ((Object[]) getInternalState(objCacheItem, "fields")).length);
396         assertEquals(0, ((Object[]) getInternalState(fileCacheItem, "fields")).length);
397     }
398 
399     @Test
400     public void testNotInterpolateFile() throws Exception {
401         Model model = new Model();
402 
403         File baseDir = new File(System.getProperty("user.dir"));
404 
405         Properties p = new Properties();
406 
407         StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
408 
409         ModelBuildingRequest config = createModelBuildingRequest(p);
410 
411         SimpleProblemCollector collector = new SimpleProblemCollector();
412         interpolator.interpolateObject(baseDir, model, new File("."), config, collector);
413         assertProblemFree(collector);
414 
415         //noinspection unchecked
416         Map<Class<?>, ?> cache = (Map<Class<?>, ?>)
417                 getField(StringSearchModelInterpolator.class, "CACHED_ENTRIES").get(null);
418 
419         Object fileCacheItem = cache.get(File.class);
420 
421         assertNotNull(fileCacheItem);
422 
423         assertEquals(0, ((Object[]) getInternalState(fileCacheItem, "fields")).length);
424     }
425 
426     @Test
427     public void testConcurrentInterpolation() throws Exception {
428         final Model model = new Model();
429 
430         final Properties p = new Properties();
431         p.setProperty("key", "value");
432         p.setProperty("key2", "value2");
433         p.setProperty("key3", "value3");
434         p.setProperty("key4", "value4");
435         p.setProperty("key5", "value5");
436 
437         final StringSearchModelInterpolator interpolator = (StringSearchModelInterpolator) createInterpolator();
438 
439         int numItems = 100;
440         final CountDownLatch countDownLatch = new CountDownLatch(1);
441 
442         List<Future<SimpleProblemCollector>> futures = new ArrayList<>();
443         for (int i = 0; i < numItems; i++) {
444             Callable<SimpleProblemCollector> future = new Callable<SimpleProblemCollector>() {
445                 public SimpleProblemCollector call() throws Exception {
446                     final ObjectWithMixedProtection obj = getValueList();
447                     final ModelBuildingRequest config = createModelBuildingRequest(p);
448 
449                     countDownLatch.await();
450                     final SimpleProblemCollector collector = new SimpleProblemCollector();
451                     interpolator.interpolateObject(obj, model, new File("."), config, collector);
452                     return collector;
453                 }
454             };
455             FutureTask<SimpleProblemCollector> task = new FutureTask<>(future);
456             futures.add(task);
457             new Thread(task).start();
458         }
459         countDownLatch.countDown(); // Start all the threads
460         for (Future<SimpleProblemCollector> result : futures) {
461             SimpleProblemCollector problemCollector =
462                     result.get(); // ArrayIndexOutOfBoundsException are typical indication of threading issues
463             assertProblemFree(problemCollector);
464         }
465     }
466 
467     private ObjectWithMixedProtection getValueList() {
468         List<String[]> values = new ArrayList<>();
469 
470         values.add(new String[] {"${key}", "${key2}"});
471         values.add(new String[] {"${key3}", "${key4}"});
472         List<String> values2 = new ArrayList<>();
473         values.add(new String[] {"${key}", "${key2}"});
474         values.add(new String[] {"${key3}", "${key4}"});
475         List<String> values3 = new ArrayList<>();
476         values.add(new String[] {"${key}", "${key2}"});
477         values.add(new String[] {"${key3}", "${key4}"});
478 
479         return new ObjectWithMixedProtection(values, values2, values3, "${key5}");
480     }
481 
482     private static final class ObjectWithStringArrayField {
483         private final String[] values;
484 
485         ObjectWithStringArrayField(String[] values) {
486             this.values = values;
487         }
488     }
489 
490     private static final class ObjectWithListField {
491         private final List<?> values;
492 
493         ObjectWithListField(List<?> values) {
494             this.values = values;
495         }
496     }
497 
498     private static final class ObjectWithMapField {
499         private final Map<?, ?> values;
500 
501         ObjectWithMapField(Map<?, ?> values) {
502             this.values = values;
503         }
504     }
505 
506     private static final class ObjectWithNotInterpolatedFile {
507         private final File f;
508 
509         ObjectWithNotInterpolatedFile(File f) {
510             this.f = f;
511         }
512     }
513 
514     @SuppressWarnings("unused")
515     private static final class ObjectWithMixedProtection {
516         private List<?> values1;
517         protected List<?> values2;
518         List<?> values3;
519         private String fooBar;
520 
521         private ObjectWithMixedProtection(List<?> values1, List<?> values2, List<?> values3) {
522             this.values1 = values1;
523             this.values2 = values2;
524             this.values3 = values3;
525         }
526 
527         private ObjectWithMixedProtection(List<?> values1, List<?> values2, List<?> values3, String fooBar) {
528             this.values1 = values1;
529             this.values2 = values2;
530             this.values3 = values3;
531             this.fooBar = fooBar;
532         }
533 
534         public String getFooBar() {
535             return fooBar;
536         }
537     }
538 
539     @Test
540     public void testFinalFieldsExcludedFromInterpolation() {
541         Properties props = new Properties();
542         props.setProperty("expression", "value");
543         DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
544         request.setUserProperties(props);
545 
546         SimpleProblemCollector problems = new SimpleProblemCollector();
547         StringSearchModelInterpolator interpolator = new StringSearchModelInterpolator();
548         interpolator.setVersionPropertiesProcessor(new DefaultModelVersionProcessor());
549         interpolator.interpolateObject(new ClassWithFinalField(), new Model(), null, request, problems);
550 
551         assertProblemFree(problems);
552     }
553 
554     static class ClassWithFinalField {
555         public static final String CONSTANT = "${expression}";
556     }
557 
558     @Test
559     public void testLocationTrackerShouldBeExcludedFromInterpolation() {
560         Properties props = new Properties();
561         props.setProperty("expression", "value");
562         DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
563         request.setUserProperties(props);
564 
565         InputSource source = new InputSource();
566         source.setLocation("${expression}");
567         source.setModelId("${expression}");
568         Model model = new Model();
569         model.setLocation("", new InputLocation(1, 1, source));
570 
571         SimpleProblemCollector problems = new SimpleProblemCollector();
572         StringSearchModelInterpolator interpolator = new StringSearchModelInterpolator();
573         interpolator.setVersionPropertiesProcessor(new DefaultModelVersionProcessor());
574         interpolator.interpolateObject(model, model, null, request, problems);
575 
576         assertProblemFree(problems);
577         assertEquals("${expression}", source.getLocation());
578         assertEquals("${expression}", source.getModelId());
579     }
580 }