1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.project.interpolation;
20
21 import javax.inject.Inject;
22 import javax.xml.stream.XMLStreamException;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.StringReader;
27 import java.io.StringWriter;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Properties;
34
35 import org.apache.maven.model.Model;
36 import org.apache.maven.model.v4.MavenStaxReader;
37 import org.apache.maven.model.v4.MavenStaxWriter;
38 import org.apache.maven.project.DefaultProjectBuilderConfiguration;
39 import org.apache.maven.project.ProjectBuilderConfiguration;
40 import org.apache.maven.project.path.PathTranslator;
41 import org.codehaus.plexus.interpolation.AbstractValueSource;
42 import org.codehaus.plexus.interpolation.InterpolationException;
43 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
44 import org.codehaus.plexus.interpolation.Interpolator;
45 import org.codehaus.plexus.interpolation.MapBasedValueSource;
46 import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
47 import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
48 import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
49 import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
50 import org.codehaus.plexus.interpolation.RecursionInterceptor;
51 import org.codehaus.plexus.interpolation.ValueSource;
52 import org.codehaus.plexus.logging.AbstractLogEnabled;
53 import org.codehaus.plexus.logging.Logger;
54 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
55 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
56
57
58
59
60
61
62 @Deprecated
63 public abstract class AbstractStringBasedModelInterpolator extends AbstractLogEnabled
64 implements ModelInterpolator, Initializable {
65
66 private static final List<String> PROJECT_PREFIXES = Arrays.asList("pom.", "project.");
67
68 private static final List<String> TRANSLATED_PATH_EXPRESSIONS;
69
70 static {
71 List<String> translatedPrefixes = new ArrayList<>();
72
73
74
75
76
77
78 translatedPrefixes.add("build.directory");
79 translatedPrefixes.add("build.outputDirectory");
80 translatedPrefixes.add("build.testOutputDirectory");
81 translatedPrefixes.add("build.sourceDirectory");
82 translatedPrefixes.add("build.testSourceDirectory");
83 translatedPrefixes.add("build.scriptSourceDirectory");
84 translatedPrefixes.add("reporting.outputDirectory");
85
86 TRANSLATED_PATH_EXPRESSIONS = translatedPrefixes;
87 }
88
89 @Inject
90 private PathTranslator pathTranslator;
91
92 private Interpolator interpolator;
93
94 private RecursionInterceptor recursionInterceptor;
95
96
97 protected AbstractStringBasedModelInterpolator(PathTranslator pathTranslator) {
98 this.pathTranslator = pathTranslator;
99 }
100
101 protected AbstractStringBasedModelInterpolator() {}
102
103 @Override
104 public Model interpolate(Model model, Map<String, ?> context) throws ModelInterpolationException {
105 return interpolate(model, context, true);
106 }
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 @Deprecated
122 @Override
123 public Model interpolate(Model model, Map<String, ?> context, boolean strict) throws ModelInterpolationException {
124 Properties props = new Properties();
125 props.putAll(context);
126
127 return interpolate(model, null, new DefaultProjectBuilderConfiguration().setExecutionProperties(props), true);
128 }
129
130 @Override
131 public Model interpolate(Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled)
132 throws ModelInterpolationException {
133 StringWriter sWriter = new StringWriter(1024);
134
135 MavenStaxWriter writer = new MavenStaxWriter();
136 try {
137 writer.write(sWriter, model.getDelegate());
138 } catch (IOException | XMLStreamException e) {
139 throw new ModelInterpolationException("Cannot serialize project model for interpolation.", e);
140 }
141
142 String serializedModel = sWriter.toString();
143 serializedModel = interpolate(serializedModel, model, projectDir, config, debugEnabled);
144
145 StringReader sReader = new StringReader(serializedModel);
146
147 MavenStaxReader modelReader = new MavenStaxReader();
148 try {
149 model = new Model(modelReader.read(sReader));
150 } catch (XMLStreamException e) {
151 throw new ModelInterpolationException(
152 "Cannot read project model from interpolating filter of serialized version.", e);
153 }
154
155 return model;
156 }
157
158
159
160
161
162
163
164
165
166
167
168
169
170 @Override
171 public String interpolate(
172 String src, Model model, final File projectDir, ProjectBuilderConfiguration config, boolean debug)
173 throws ModelInterpolationException {
174 try {
175 List<ValueSource> valueSources = createValueSources(model, projectDir, config);
176 List<InterpolationPostProcessor> postProcessors = createPostProcessors(model, projectDir, config);
177
178 return interpolateInternal(src, valueSources, postProcessors, debug);
179 } finally {
180 interpolator.clearAnswers();
181 }
182 }
183
184 protected List<ValueSource> createValueSources(
185 final Model model, final File projectDir, final ProjectBuilderConfiguration config) {
186 String timestampFormat = DEFAULT_BUILD_TIMESTAMP_FORMAT;
187
188 Properties modelProperties = model.getProperties();
189 if (modelProperties != null) {
190 timestampFormat = modelProperties.getProperty(BUILD_TIMESTAMP_FORMAT_PROPERTY, timestampFormat);
191 }
192
193 ValueSource modelValueSource1 = new PrefixedObjectValueSource(PROJECT_PREFIXES, model, false);
194 ValueSource modelValueSource2 = new ObjectBasedValueSource(model);
195
196 ValueSource basedirValueSource = new PrefixedValueSourceWrapper(
197 new AbstractValueSource(false) {
198
199 @Override
200 public Object getValue(String expression) {
201 if (projectDir != null && "basedir".equals(expression)) {
202 return projectDir.getAbsolutePath();
203 }
204 return null;
205 }
206 },
207 PROJECT_PREFIXES,
208 true);
209 ValueSource baseUriValueSource = new PrefixedValueSourceWrapper(
210 new AbstractValueSource(false) {
211
212 @Override
213 public Object getValue(String expression) {
214 if (projectDir != null && "baseUri".equals(expression)) {
215 return projectDir.getAbsoluteFile().toPath().toUri().toASCIIString();
216 }
217 return null;
218 }
219 },
220 PROJECT_PREFIXES,
221 false);
222
223 List<ValueSource> valueSources = new ArrayList<>(9);
224
225
226 valueSources.add(basedirValueSource);
227 valueSources.add(baseUriValueSource);
228 valueSources.add(new BuildTimestampValueSource(config.getBuildStartTime(), timestampFormat));
229 valueSources.add(modelValueSource1);
230 valueSources.add(new MapBasedValueSource(config.getUserProperties()));
231 valueSources.add(new MapBasedValueSource(modelProperties));
232 valueSources.add(new MapBasedValueSource(config.getExecutionProperties()));
233 valueSources.add(new AbstractValueSource(false) {
234
235 @Override
236 public Object getValue(String expression) {
237 return config.getExecutionProperties().getProperty("env." + expression);
238 }
239 });
240 valueSources.add(modelValueSource2);
241
242 return valueSources;
243 }
244
245 protected List<InterpolationPostProcessor> createPostProcessors(
246 final Model model, final File projectDir, final ProjectBuilderConfiguration config) {
247 return Collections.singletonList(new PathTranslatingPostProcessor(
248 PROJECT_PREFIXES, TRANSLATED_PATH_EXPRESSIONS, projectDir, pathTranslator));
249 }
250
251 @SuppressWarnings("unchecked")
252 protected String interpolateInternal(
253 String src, List<ValueSource> valueSources, List<InterpolationPostProcessor> postProcessors, boolean debug)
254 throws ModelInterpolationException {
255 if (!src.contains("${")) {
256 return src;
257 }
258
259 Logger logger = getLogger();
260
261 String result = src;
262 synchronized (this) {
263 for (ValueSource vs : valueSources) {
264 interpolator.addValueSource(vs);
265 }
266
267 for (InterpolationPostProcessor postProcessor : postProcessors) {
268 interpolator.addPostProcessor(postProcessor);
269 }
270
271 try {
272 try {
273 result = interpolator.interpolate(result, recursionInterceptor);
274 } catch (InterpolationException e) {
275 throw new ModelInterpolationException(e.getMessage(), e);
276 }
277
278 if (debug) {
279 List<Object> feedback = interpolator.getFeedback();
280 if (feedback != null && !feedback.isEmpty()) {
281 logger.debug("Maven encountered the following problems during initial POM interpolation:");
282
283 Object last = null;
284 for (Object next : feedback) {
285 if (next instanceof Throwable throwable) {
286 if (last == null) {
287 logger.debug("", throwable);
288 } else {
289 logger.debug(String.valueOf(last), throwable);
290 }
291 } else {
292 if (last != null) {
293 logger.debug(String.valueOf(last));
294 }
295
296 last = next;
297 }
298 }
299
300 if (last != null) {
301 logger.debug(String.valueOf(last));
302 }
303 }
304 }
305
306 interpolator.clearFeedback();
307 } finally {
308 for (ValueSource vs : valueSources) {
309 interpolator.removeValuesSource(vs);
310 }
311
312 for (InterpolationPostProcessor postProcessor : postProcessors) {
313 interpolator.removePostProcessor(postProcessor);
314 }
315 }
316 }
317
318 return result;
319 }
320
321 protected RecursionInterceptor getRecursionInterceptor() {
322 return recursionInterceptor;
323 }
324
325 protected void setRecursionInterceptor(RecursionInterceptor recursionInterceptor) {
326 this.recursionInterceptor = recursionInterceptor;
327 }
328
329 protected abstract Interpolator createInterpolator();
330
331 @Override
332 public void initialize() throws InitializationException {
333 interpolator = createInterpolator();
334 recursionInterceptor = new PrefixAwareRecursionInterceptor(PROJECT_PREFIXES);
335 }
336
337 protected final Interpolator getInterpolator() {
338 return interpolator;
339 }
340 }