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