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 javax.inject.Inject;
22  
23  import java.io.File;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Properties;
30  
31  import org.apache.maven.model.Model;
32  import org.apache.maven.model.building.ModelBuildingRequest;
33  import org.apache.maven.model.building.ModelProblemCollector;
34  import org.apache.maven.model.path.PathTranslator;
35  import org.apache.maven.model.path.UrlNormalizer;
36  import org.codehaus.plexus.interpolation.AbstractValueSource;
37  import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
38  import org.codehaus.plexus.interpolation.MapBasedValueSource;
39  import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
40  import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
41  import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
42  import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
43  import org.codehaus.plexus.interpolation.RecursionInterceptor;
44  import org.codehaus.plexus.interpolation.ValueSource;
45  
46  /**
47   * Use a regular expression search to find and resolve expressions within the POM.
48   *
49   * @deprecated use {@code org.apache.maven.api.services.ModelBuilder} instead
50   */
51  @Deprecated(since = "4.0.0")
52  public abstract class AbstractStringBasedModelInterpolator implements ModelInterpolator {
53  
54      /**
55       * Local mirror of {@code org.apache.maven.api.FULL_EXTERNAL_INTERPOLATION_PROPERTY}.
56       * This compat module does not depend on {@code maven-api-core}, so the value is duplicated here.
57       */
58      private static final String FULL_EXTERNAL_INTERPOLATION_PROPERTY = "maven.model.dependencyInterpolation.full";
59  
60      private static final List<String> PROJECT_PREFIXES = Arrays.asList("pom.", "project.");
61  
62      private static final Collection<String> TRANSLATED_PATH_EXPRESSIONS;
63  
64      static {
65          Collection<String> translatedPrefixes = new HashSet<>();
66  
67          // MNG-1927, MNG-2124, MNG-3355:
68          // If the build section is present and the project directory is non-null, we should make
69          // sure interpolation of the directories below uses translated paths.
70          // Afterward, we'll double back and translate any paths that weren't covered during interpolation via the
71          // code below...
72          translatedPrefixes.add("build.directory");
73          translatedPrefixes.add("build.outputDirectory");
74          translatedPrefixes.add("build.testOutputDirectory");
75          translatedPrefixes.add("build.sourceDirectory");
76          translatedPrefixes.add("build.testSourceDirectory");
77          translatedPrefixes.add("build.scriptSourceDirectory");
78          translatedPrefixes.add("reporting.outputDirectory");
79  
80          TRANSLATED_PATH_EXPRESSIONS = translatedPrefixes;
81      }
82  
83      @Inject
84      private PathTranslator pathTranslator;
85  
86      @Inject
87      private UrlNormalizer urlNormalizer;
88  
89      @Inject
90      private ModelVersionProcessor versionProcessor;
91  
92      public AbstractStringBasedModelInterpolator() {}
93  
94      public AbstractStringBasedModelInterpolator setPathTranslator(PathTranslator pathTranslator) {
95          this.pathTranslator = pathTranslator;
96          return this;
97      }
98  
99      public AbstractStringBasedModelInterpolator setUrlNormalizer(UrlNormalizer urlNormalizer) {
100         this.urlNormalizer = urlNormalizer;
101         return this;
102     }
103 
104     public AbstractStringBasedModelInterpolator setVersionPropertiesProcessor(ModelVersionProcessor processor) {
105         this.versionProcessor = processor;
106         return this;
107     }
108 
109     protected List<ValueSource> createValueSources(
110             final Model model,
111             final File projectDir,
112             final ModelBuildingRequest config,
113             final ModelProblemCollector problems) {
114         Properties modelProperties = model.getProperties();
115 
116         ValueSource modelValueSource1 = new PrefixedObjectValueSource(PROJECT_PREFIXES, model, false);
117         if (config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
118             modelValueSource1 = new ProblemDetectingValueSource(modelValueSource1, "pom.", "project.", problems);
119         }
120 
121         ValueSource modelValueSource2 = new ObjectBasedValueSource(model);
122         if (config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
123             modelValueSource2 = new ProblemDetectingValueSource(modelValueSource2, "", "project.", problems);
124         }
125 
126         // NOTE: Order counts here!
127         List<ValueSource> valueSources = new ArrayList<>(9);
128 
129         if (projectDir != null) {
130             ValueSource basedirValueSource = new PrefixedValueSourceWrapper(
131                     new AbstractValueSource(false) {
132                         @Override
133                         public Object getValue(String expression) {
134                             if ("basedir".equals(expression)) {
135                                 return projectDir.getAbsolutePath();
136                             }
137                             return null;
138                         }
139                     },
140                     PROJECT_PREFIXES,
141                     true);
142             valueSources.add(basedirValueSource);
143 
144             ValueSource baseUriValueSource = new PrefixedValueSourceWrapper(
145                     new AbstractValueSource(false) {
146                         @Override
147                         public Object getValue(String expression) {
148                             if ("baseUri".equals(expression)) {
149                                 return projectDir
150                                         .getAbsoluteFile()
151                                         .toPath()
152                                         .toUri()
153                                         .toASCIIString();
154                             }
155                             return null;
156                         }
157                     },
158                     PROJECT_PREFIXES,
159                     false);
160             valueSources.add(baseUriValueSource);
161             valueSources.add(new BuildTimestampValueSource(config.getBuildStartTime(), modelProperties));
162         }
163 
164         valueSources.add(modelValueSource1);
165 
166         // Models built at VALIDATION_LEVEL_MINIMAL are the models Maven builds while resolving
167         // dependency, parent and BOM-import POMs from a repository, not the operator's own
168         // project. Such models interpolate only against their own properties and a small set
169         // of environment-independent expressions; everything else in the user/system property
170         // space stays uninterpolated. Operator project builds use a higher validation level and
171         // keep the full set of value sources, unchanged from previous behavior.
172         boolean restricted = restrictExternalModelInterpolation(config);
173 
174         ValueSource userPropertiesValueSource = new MapBasedValueSource(config.getUserProperties());
175         valueSources.add(restricted ? restrictToSafeExpressions(userPropertiesValueSource) : userPropertiesValueSource);
176 
177         // Overwrite existing values in model properties. Otherwise, it's not possible
178         // to define them via command line e.g.: mvn -Drevision=6.5.7 ...
179         versionProcessor.overwriteModelProperties(modelProperties, config);
180         valueSources.add(new MapBasedValueSource(modelProperties));
181 
182         ValueSource systemPropertiesValueSource = new MapBasedValueSource(config.getSystemProperties());
183         valueSources.add(
184                 restricted ? restrictToSafeExpressions(systemPropertiesValueSource) : systemPropertiesValueSource);
185 
186         if (!restricted) {
187             valueSources.add(new AbstractValueSource(false) {
188                 @Override
189                 public Object getValue(String expression) {
190                     return config.getSystemProperties().getProperty("env." + expression);
191                 }
192             });
193         }
194 
195         valueSources.add(modelValueSource2);
196 
197         return valueSources;
198     }
199 
200     private static boolean restrictExternalModelInterpolation(ModelBuildingRequest config) {
201         return config.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0
202                 && !Boolean.parseBoolean(config.getSystemProperties().getProperty(FULL_EXTERNAL_INTERPOLATION_PROPERTY))
203                 && !Boolean.parseBoolean(config.getUserProperties().getProperty(FULL_EXTERNAL_INTERPOLATION_PROPERTY));
204     }
205 
206     private static ValueSource restrictToSafeExpressions(ValueSource source) {
207         return new AbstractValueSource(false) {
208             @Override
209             public Object getValue(String expression) {
210                 return isSafeExternalExpression(expression) ? source.getValue(expression) : null;
211             }
212         };
213     }
214 
215     /**
216      * Expressions that models built at {@link ModelBuildingRequest#VALIDATION_LEVEL_MINIMAL}
217      * may still resolve from the session properties: JVM- and Maven-defined properties, plus
218      * the CI-friendly version properties (MNG-5895). All other expressions are left literal.
219      */
220     private static boolean isSafeExternalExpression(String expression) {
221         return expression.startsWith("java.")
222                 || expression.startsWith("os.")
223                 || expression.startsWith("maven.")
224                 || "file.separator".equals(expression)
225                 || "path.separator".equals(expression)
226                 || "line.separator".equals(expression)
227                 || "revision".equals(expression)
228                 || "changelist".equals(expression)
229                 || "sha1".equals(expression);
230     }
231 
232     protected List<? extends InterpolationPostProcessor> createPostProcessors(
233             final Model model, final File projectDir, final ModelBuildingRequest config) {
234         List<InterpolationPostProcessor> processors = new ArrayList<>(2);
235         if (projectDir != null) {
236             processors.add(new PathTranslatingPostProcessor(
237                     PROJECT_PREFIXES, TRANSLATED_PATH_EXPRESSIONS,
238                     projectDir, pathTranslator));
239         }
240         processors.add(new UrlNormalizingPostProcessor(urlNormalizer));
241         return processors;
242     }
243 
244     protected RecursionInterceptor createRecursionInterceptor() {
245         return new PrefixAwareRecursionInterceptor(PROJECT_PREFIXES);
246     }
247 }