View Javadoc
1   package org.apache.maven.model.interpolation;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Properties;
29  
30  import org.apache.maven.model.Model;
31  import org.apache.maven.model.building.ModelBuildingRequest;
32  import org.apache.maven.model.building.ModelProblem.Severity;
33  import org.apache.maven.model.building.ModelProblem.Version;
34  import org.apache.maven.model.building.ModelProblemCollector;
35  import org.apache.maven.model.building.ModelProblemCollectorRequest;
36  import org.apache.maven.model.path.PathTranslator;
37  import org.apache.maven.model.path.UrlNormalizer;
38  import org.codehaus.plexus.component.annotations.Requirement;
39  import org.codehaus.plexus.interpolation.AbstractValueSource;
40  import org.codehaus.plexus.interpolation.InterpolationException;
41  import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
42  import org.codehaus.plexus.interpolation.Interpolator;
43  import org.codehaus.plexus.interpolation.MapBasedValueSource;
44  import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
45  import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
46  import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
47  import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
48  import org.codehaus.plexus.interpolation.RecursionInterceptor;
49  import org.codehaus.plexus.interpolation.ValueSource;
50  
51  /**
52   * Use a regular expression search to find and resolve expressions within the POM.
53   *
54   * @author jdcasey Created on Feb 3, 2005
55   */
56  public abstract class AbstractStringBasedModelInterpolator
57      implements ModelInterpolator
58  {
59      public static final String SHA1_PROPERTY = "sha1";
60  
61      public static final String CHANGELIST_PROPERTY = "changelist";
62  
63      public static final String REVISION_PROPERTY = "revision";
64  
65      private static final List<String> PROJECT_PREFIXES = Arrays.asList( "pom.", "project." );
66  
67      private static final Collection<String> TRANSLATED_PATH_EXPRESSIONS;
68  
69      static
70      {
71          Collection<String> translatedPrefixes = new HashSet<>();
72  
73          // MNG-1927, MNG-2124, MNG-3355:
74          // If the build section is present and the project directory is non-null, we should make
75          // sure interpolation of the directories below uses translated paths.
76          // Afterward, we'll double back and translate any paths that weren't covered during interpolation via the
77          // code below...
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      @Requirement
90      private PathTranslator pathTranslator;
91  
92      @Requirement
93      private UrlNormalizer urlNormalizer;
94  
95      private Interpolator interpolator;
96  
97      private RecursionInterceptor recursionInterceptor;
98  
99      public AbstractStringBasedModelInterpolator()
100     {
101         interpolator = createInterpolator();
102         recursionInterceptor = new PrefixAwareRecursionInterceptor( PROJECT_PREFIXES );
103     }
104 
105     public AbstractStringBasedModelInterpolator setPathTranslator( PathTranslator pathTranslator )
106     {
107         this.pathTranslator = pathTranslator;
108         return this;
109     }
110 
111     public AbstractStringBasedModelInterpolator setUrlNormalizer( UrlNormalizer urlNormalizer )
112     {
113         this.urlNormalizer = urlNormalizer;
114         return this;
115     }
116 
117     protected List<ValueSource> createValueSources( final Model model, final File projectDir,
118                                                     final ModelBuildingRequest config,
119                                                     final ModelProblemCollector problems )
120     {
121         Properties modelProperties = model.getProperties();
122 
123         ValueSource modelValueSource1 = new PrefixedObjectValueSource( PROJECT_PREFIXES, model, false );
124         if ( config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
125         {
126             modelValueSource1 = new ProblemDetectingValueSource( modelValueSource1, "pom.", "project.", problems );
127         }
128 
129         ValueSource modelValueSource2 = new ObjectBasedValueSource( model );
130         if ( config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
131         {
132             modelValueSource2 = new ProblemDetectingValueSource( modelValueSource2, "", "project.", problems );
133         }
134 
135         // NOTE: Order counts here!
136         List<ValueSource> valueSources = new ArrayList<>( 9 );
137 
138         if ( projectDir != null )
139         {
140             ValueSource basedirValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource( false )
141             {
142                 @Override
143                 public Object getValue( String expression )
144                 {
145                     if ( "basedir".equals( expression ) )
146                     {
147                         return projectDir.getAbsolutePath();
148                     }
149                     return null;
150                 }
151             }, PROJECT_PREFIXES, true );
152             valueSources.add( basedirValueSource );
153 
154             ValueSource baseUriValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource( false )
155             {
156                 @Override
157                 public Object getValue( String expression )
158                 {
159                     if ( "baseUri".equals( expression ) )
160                     {
161                         return projectDir.getAbsoluteFile().toPath().toUri().toASCIIString();
162                     }
163                     return null;
164                 }
165             }, PROJECT_PREFIXES, false );
166             valueSources.add( baseUriValueSource );
167             valueSources.add( new BuildTimestampValueSource( config.getBuildStartTime(), modelProperties ) );
168         }
169 
170         valueSources.add( modelValueSource1 );
171 
172         valueSources.add( new MapBasedValueSource( config.getUserProperties() ) );
173 
174         // Overwrite existing values in model properties. Otherwise it's not possible
175         // to define the version via command line: mvn -Drevision=6.5.7 ...
176         if ( config.getSystemProperties().containsKey( REVISION_PROPERTY ) )
177         {
178             modelProperties.put( REVISION_PROPERTY, config.getSystemProperties().get( REVISION_PROPERTY ) );
179         }
180         if ( config.getSystemProperties().containsKey( CHANGELIST_PROPERTY ) )
181         {
182             modelProperties.put( CHANGELIST_PROPERTY, config.getSystemProperties().get( CHANGELIST_PROPERTY ) );
183         }
184         if ( config.getSystemProperties().containsKey( SHA1_PROPERTY ) )
185         {
186             modelProperties.put( SHA1_PROPERTY, config.getSystemProperties().get( SHA1_PROPERTY ) );
187         }
188         valueSources.add( new MapBasedValueSource( modelProperties ) );
189 
190         valueSources.add( new MapBasedValueSource( config.getSystemProperties() ) );
191 
192         valueSources.add( new AbstractValueSource( false )
193         {
194             @Override
195             public Object getValue( String expression )
196             {
197                 return config.getSystemProperties().getProperty( "env." + expression );
198             }
199         } );
200 
201         valueSources.add( modelValueSource2 );
202 
203         return valueSources;
204     }
205 
206     protected List<? extends InterpolationPostProcessor> createPostProcessors( final Model model,
207                                                                                final File projectDir,
208                                                                                final ModelBuildingRequest config )
209     {
210         List<InterpolationPostProcessor> processors = new ArrayList<>( 2 );
211         if ( projectDir != null )
212         {
213             processors.add( new PathTranslatingPostProcessor( PROJECT_PREFIXES, TRANSLATED_PATH_EXPRESSIONS,
214                                                               projectDir, pathTranslator ) );
215         }
216         processors.add( new UrlNormalizingPostProcessor( urlNormalizer ) );
217         return processors;
218     }
219 
220     protected String interpolateInternal( String src, List<? extends ValueSource> valueSources,
221                                           List<? extends InterpolationPostProcessor> postProcessors,
222                                           ModelProblemCollector problems )
223     {
224         if ( !src.contains( "${" ) )
225         {
226             return src;
227         }
228 
229         String result = src;
230         synchronized ( this )
231         {
232 
233             for ( ValueSource vs : valueSources )
234             {
235                 interpolator.addValueSource( vs );
236             }
237 
238             for ( InterpolationPostProcessor postProcessor : postProcessors )
239             {
240                 interpolator.addPostProcessor( postProcessor );
241             }
242 
243             try
244             {
245                 try
246                 {
247                     result = interpolator.interpolate( result, recursionInterceptor );
248                 }
249                 catch ( InterpolationException e )
250                 {
251                     problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
252                         .setMessage( e.getMessage() ).setException( e ) );
253                 }
254 
255                 interpolator.clearFeedback();
256             }
257             finally
258             {
259                 for ( ValueSource vs : valueSources )
260                 {
261                     interpolator.removeValuesSource( vs );
262                 }
263 
264                 for ( InterpolationPostProcessor postProcessor : postProcessors )
265                 {
266                     interpolator.removePostProcessor( postProcessor );
267                 }
268             }
269         }
270 
271         return result;
272     }
273 
274     protected RecursionInterceptor getRecursionInterceptor()
275     {
276         return recursionInterceptor;
277     }
278 
279     protected void setRecursionInterceptor( RecursionInterceptor recursionInterceptor )
280     {
281         this.recursionInterceptor = recursionInterceptor;
282     }
283 
284     protected abstract Interpolator createInterpolator();
285 
286     protected final Interpolator getInterpolator()
287     {
288         return interpolator;
289     }
290 
291 }