View Javadoc
1   package org.apache.maven.shared.filtering;
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.io.IOException;
24  import java.io.Reader;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.LinkedHashSet;
28  import java.util.List;
29  import java.util.Properties;
30  import java.util.TreeSet;
31  
32  import org.apache.maven.execution.MavenSession;
33  import org.apache.maven.project.MavenProject;
34  import org.apache.maven.settings.Settings;
35  import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
36  import org.codehaus.plexus.interpolation.Interpolator;
37  import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
38  import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
39  import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
40  import org.codehaus.plexus.interpolation.RecursionInterceptor;
41  import org.codehaus.plexus.interpolation.SimpleRecursionInterceptor;
42  import org.codehaus.plexus.interpolation.SingleResponseValueSource;
43  import org.codehaus.plexus.interpolation.ValueSource;
44  import org.codehaus.plexus.interpolation.multi.MultiDelimiterStringSearchInterpolator;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  
48  class BaseFilter
49      implements DefaultFilterInfo
50  {
51      private final Logger logger = LoggerFactory.getLogger( getClass() );
52  
53      protected Logger getLogger()
54      {
55          return logger;
56      }
57  
58      @Override
59      public List<FilterWrapper> getDefaultFilterWrappers( final MavenProject mavenProject,
60                                                                     List<String> filters,
61                                                                     final boolean escapedBackslashesInFilePath,
62                                                                     MavenSession mavenSession,
63                                                                     MavenResourcesExecution mavenResourcesExecution )
64                                                                         throws MavenFilteringException
65      {
66  
67          MavenResourcesExecution mre =
68              mavenResourcesExecution == null ? new MavenResourcesExecution() : mavenResourcesExecution.copyOf();
69  
70          mre.setMavenProject( mavenProject );
71          mre.setMavenSession( mavenSession );
72          mre.setFilters( filters );
73          mre.setEscapedBackslashesInFilePath( escapedBackslashesInFilePath );
74  
75          return getDefaultFilterWrappers( mre );
76  
77      }
78  
79      @Override
80      public List<FilterWrapper> getDefaultFilterWrappers( final AbstractMavenFilteringRequest request )
81          throws MavenFilteringException
82      {
83          // backup values
84          boolean supportMultiLineFiltering = request.isSupportMultiLineFiltering();
85  
86          request.setSupportMultiLineFiltering( supportMultiLineFiltering );
87  
88          // Here we build some properties which will be used to read some properties files
89          // to interpolate the expression ${ } in this properties file
90  
91          // Take a copy of filterProperties to ensure that evaluated filterTokens are not propagated
92          // to subsequent filter files. Note: this replicates current behaviour and seems to make sense.
93  
94          final Properties baseProps = new Properties();
95  
96          // Project properties
97          if ( request.getMavenProject() != null )
98          {
99              baseProps.putAll( request.getMavenProject().getProperties() == null ? Collections.emptyMap()
100                             : request.getMavenProject().getProperties() );
101         }
102         // TODO this is NPE free but do we consider this as normal
103         // or do we have to throw an MavenFilteringException with mavenSession cannot be null
104         //
105         // khmarbaise: 2016-05-21:
106         // If we throw an MavenFilteringException tests will fail which is
107         // caused by for example:
108         // void copyFile( File from, final File to, boolean filtering, List<FileUtils.FilterWrapper> filterWrappers,
109         // String encoding )
110         // in MavenFileFilter interface where no MavenSession is given.
111         // So changing here to throw a MavenFilteringException would make
112         // it necessary to change the interface or we need to find a better solution.
113         //
114         if ( request.getMavenSession() != null )
115         {
116             // User properties have precedence over system properties
117             baseProps.putAll( request.getMavenSession().getSystemProperties() );
118             baseProps.putAll( request.getMavenSession().getUserProperties() );
119         }
120 
121         // now we build properties to use for resources interpolation
122 
123         final Properties filterProperties = new Properties();
124 
125         File basedir = request.getMavenProject() != null ? request.getMavenProject().getBasedir() : new File( "." );
126 
127         loadProperties( filterProperties, basedir, request.getFileFilters(), baseProps );
128         if ( filterProperties.size() < 1 )
129         {
130             filterProperties.putAll( baseProps );
131         }
132 
133         if ( request.getMavenProject() != null )
134         {
135             if ( request.isInjectProjectBuildFilters() )
136             {
137                 List<String> buildFilters = new ArrayList<>( request.getMavenProject().getBuild().getFilters() );
138 
139                 // JDK-8015656: (coll) unexpected NPE from removeAll
140                 if ( request.getFileFilters() != null )
141                 {
142                     buildFilters.removeAll( request.getFileFilters() );
143                 }
144 
145                 loadProperties( filterProperties, basedir, buildFilters, baseProps );
146             }
147 
148             // Project properties
149             filterProperties.putAll( request.getMavenProject().getProperties() == null ? Collections.emptyMap()
150                             : request.getMavenProject().getProperties() );
151         }
152         if ( request.getMavenSession() != null )
153         {
154             // User properties have precedence over system properties
155             filterProperties.putAll( request.getMavenSession().getSystemProperties() );
156             filterProperties.putAll( request.getMavenSession().getUserProperties() );
157         }
158 
159         if ( request.getAdditionalProperties() != null )
160         {
161             // additional properties wins
162             filterProperties.putAll( request.getAdditionalProperties() );
163         }
164 
165         List<FilterWrapper> defaultFilterWrappers = new ArrayList<>( request.getDelimiters().size() + 1 );
166 
167         if ( getLogger().isDebugEnabled() )
168         {
169             getLogger().debug( "properties used:" );
170             for ( String s : new TreeSet<>( filterProperties.stringPropertyNames() ) )
171             {
172                 getLogger().debug( s + ": " + filterProperties.getProperty( s ) );
173             }
174         }
175 
176         final ValueSource propertiesValueSource = new PropertiesBasedValueSource( filterProperties );
177 
178         FilterWrapper wrapper =
179             new Wrapper( request.getDelimiters(), request.getMavenProject(), request.getMavenSession(),
180                          propertiesValueSource, request.getProjectStartExpressions(), request.getEscapeString(),
181                          request.isEscapeWindowsPaths(), request.isSupportMultiLineFiltering() );
182 
183         defaultFilterWrappers.add( wrapper );
184 
185         return defaultFilterWrappers;
186     }
187 
188     /**
189      * default visibility only for testing reason !
190      */
191     void loadProperties( Properties filterProperties, File basedir, List<String> propertiesFilePaths,
192                          Properties baseProps )
193                              throws MavenFilteringException
194     {
195         if ( propertiesFilePaths != null )
196         {
197             Properties workProperties = new Properties();
198             workProperties.putAll( baseProps );
199 
200             for ( String filterFile : propertiesFilePaths )
201             {
202                 if (  filterFile == null || filterFile.trim().isEmpty() )
203                 {
204                     // skip empty file name
205                     continue;
206                 }
207                 try
208                 {
209                     File propFile = FilteringUtils.resolveFile( basedir, filterFile );
210                     Properties properties = PropertyUtils.loadPropertyFile( propFile, workProperties, getLogger() );
211                     filterProperties.putAll( properties );
212                     workProperties.putAll( properties );
213                 }
214                 catch ( IOException e )
215                 {
216                     throw new MavenFilteringException( "Error loading property file '" + filterFile + "'", e );
217                 }
218             }
219         }
220     }
221 
222     private static final class Wrapper
223         extends FilterWrapper
224     {
225 
226         private LinkedHashSet<String> delimiters;
227 
228         private MavenProject project;
229 
230         private ValueSource propertiesValueSource;
231 
232         private List<String> projectStartExpressions;
233 
234         private String escapeString;
235 
236         private boolean escapeWindowsPaths;
237 
238         private final MavenSession mavenSession;
239 
240         private boolean supportMultiLineFiltering;
241 
242         Wrapper( LinkedHashSet<String> delimiters, MavenProject project, MavenSession mavenSession,
243                  ValueSource propertiesValueSource, List<String> projectStartExpressions, String escapeString,
244                  boolean escapeWindowsPaths, boolean supportMultiLineFiltering )
245         {
246             super();
247             this.delimiters = delimiters;
248             this.project = project;
249             this.mavenSession = mavenSession;
250             this.propertiesValueSource = propertiesValueSource;
251             this.projectStartExpressions = projectStartExpressions;
252             this.escapeString = escapeString;
253             this.escapeWindowsPaths = escapeWindowsPaths;
254             this.supportMultiLineFiltering = supportMultiLineFiltering;
255         }
256 
257         @Override
258         public Reader getReader( Reader reader )
259         {
260             Interpolator interpolator = createInterpolator( delimiters, projectStartExpressions, propertiesValueSource,
261                                                             project, mavenSession, escapeString, escapeWindowsPaths );
262 
263             MultiDelimiterInterpolatorFilterReaderLineEnding filterReader =
264                 new MultiDelimiterInterpolatorFilterReaderLineEnding( reader, interpolator, supportMultiLineFiltering );
265 
266             final RecursionInterceptor ri;
267             if ( projectStartExpressions != null && !projectStartExpressions.isEmpty() )
268             {
269                 ri = new PrefixAwareRecursionInterceptor( projectStartExpressions, true );
270             }
271             else
272             {
273                 ri = new SimpleRecursionInterceptor();
274             }
275 
276             filterReader.setRecursionInterceptor( ri );
277             filterReader.setDelimiterSpecs( delimiters );
278 
279             filterReader.setInterpolateWithPrefixPattern( false );
280             filterReader.setEscapeString( escapeString );
281 
282             return filterReader;
283         }
284 
285     }
286 
287     private static Interpolator createInterpolator( LinkedHashSet<String> delimiters,
288                                                     List<String> projectStartExpressions,
289                                                     ValueSource propertiesValueSource, MavenProject project,
290                                                     MavenSession mavenSession, String escapeString,
291                                                     boolean escapeWindowsPaths )
292     {
293         MultiDelimiterStringSearchInterpolator interpolator = new MultiDelimiterStringSearchInterpolator();
294         interpolator.setDelimiterSpecs( delimiters );
295 
296         interpolator.addValueSource( propertiesValueSource );
297 
298         if ( project != null )
299         {
300             interpolator.addValueSource( new PrefixedObjectValueSource( projectStartExpressions, project, true ) );
301         }
302 
303         if ( mavenSession != null )
304         {
305             interpolator.addValueSource( new PrefixedObjectValueSource( "session", mavenSession ) );
306 
307             final Settings settings = mavenSession.getSettings();
308             if ( settings != null )
309             {
310                 interpolator.addValueSource( new PrefixedObjectValueSource( "settings", settings ) );
311                 interpolator.addValueSource( new SingleResponseValueSource( "localRepository",
312                                                                             settings.getLocalRepository() ) );
313             }
314         }
315 
316         interpolator.setEscapeString( escapeString );
317 
318         if ( escapeWindowsPaths )
319         {
320             interpolator.addPostProcessor( new InterpolationPostProcessor()
321             {
322                 @Override
323                 public Object execute( String expression, Object value )
324                 {
325                     if ( value instanceof String )
326                     {
327                         return FilteringUtils.escapeWindowsPath( (String) value );
328                     }
329 
330                     return value;
331                 }
332             } );
333         }
334         return interpolator;
335     }
336 }