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