View Javadoc
1   package org.apache.maven.settings.building;
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.StringReader;
25  import java.io.StringWriter;
26  import java.util.Collections;
27  import java.util.List;
28  import java.util.Map;
29  
30  import org.apache.maven.building.FileSource;
31  import org.apache.maven.building.Source;
32  import org.apache.maven.settings.Settings;
33  import org.apache.maven.settings.TrackableBase;
34  import org.apache.maven.settings.io.SettingsParseException;
35  import org.apache.maven.settings.io.SettingsReader;
36  import org.apache.maven.settings.io.SettingsWriter;
37  import org.apache.maven.settings.merge.MavenSettingsMerger;
38  import org.apache.maven.settings.validation.SettingsValidator;
39  import org.codehaus.plexus.component.annotations.Component;
40  import org.codehaus.plexus.component.annotations.Requirement;
41  import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
42  import org.codehaus.plexus.interpolation.InterpolationException;
43  import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
44  import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
45  import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
46  
47  /**
48   * Builds the effective settings from a user settings file and/or a global settings file.
49   *
50   * @author Benjamin Bentmann
51   */
52  @Component( role = SettingsBuilder.class )
53  public class DefaultSettingsBuilder
54      implements SettingsBuilder
55  {
56  
57      @Requirement
58      private SettingsReader settingsReader;
59  
60      @Requirement
61      private SettingsWriter settingsWriter;
62  
63      @Requirement
64      private SettingsValidator settingsValidator;
65  
66      private MavenSettingsMerger settingsMerger = new MavenSettingsMerger();
67  
68      public DefaultSettingsBuilder setSettingsReader( SettingsReader settingsReader )
69      {
70          this.settingsReader = settingsReader;
71          return this;
72      }
73  
74      public DefaultSettingsBuilder setSettingsWriter( SettingsWriter settingsWriter )
75      {
76          this.settingsWriter = settingsWriter;
77          return this;
78      }
79  
80      public DefaultSettingsBuilder setSettingsValidator( SettingsValidator settingsValidator )
81      {
82          this.settingsValidator = settingsValidator;
83          return this;
84      }
85  
86      @Override
87      public SettingsBuildingResult build( SettingsBuildingRequest request )
88          throws SettingsBuildingException
89      {
90          DefaultSettingsProblemCollector problems = new DefaultSettingsProblemCollector( null );
91  
92          Source globalSettingsSource =
93              getSettingsSource( request.getGlobalSettingsFile(), request.getGlobalSettingsSource() );
94          Settings globalSettings = readSettings( globalSettingsSource, request, problems );
95  
96          Source userSettingsSource =
97              getSettingsSource( request.getUserSettingsFile(), request.getUserSettingsSource() );
98          Settings userSettings = readSettings( userSettingsSource, request, problems );
99  
100         settingsMerger.merge( userSettings, globalSettings, TrackableBase.GLOBAL_LEVEL );
101 
102         problems.setSource( "" );
103 
104         userSettings = interpolate( userSettings, request, problems );
105 
106         // for the special case of a drive-relative Windows path, make sure it's absolute to save plugins from trouble
107         String localRepository = userSettings.getLocalRepository();
108         if ( localRepository != null && localRepository.length() > 0 )
109         {
110             File file = new File( localRepository );
111             if ( !file.isAbsolute() && file.getPath().startsWith( File.separator ) )
112             {
113                 userSettings.setLocalRepository( file.getAbsolutePath() );
114             }
115         }
116 
117         if ( hasErrors( problems.getProblems() ) )
118         {
119             throw new SettingsBuildingException( problems.getProblems() );
120         }
121 
122         return new DefaultSettingsBuildingResult( userSettings, problems.getProblems() );
123     }
124 
125     private boolean hasErrors( List<SettingsProblem> problems )
126     {
127         if ( problems != null )
128         {
129             for ( SettingsProblem problem : problems )
130             {
131                 if ( SettingsProblem.Severity.ERROR.compareTo( problem.getSeverity() ) >= 0 )
132                 {
133                     return true;
134                 }
135             }
136         }
137 
138         return false;
139     }
140 
141     private Source getSettingsSource( File settingsFile, Source settingsSource )
142     {
143         if ( settingsSource != null )
144         {
145             return settingsSource;
146         }
147         else if ( settingsFile != null && settingsFile.exists() )
148         {
149             return new FileSource( settingsFile );
150         }
151         return null;
152     }
153 
154     private Settings readSettings( Source settingsSource, SettingsBuildingRequest request,
155                                    DefaultSettingsProblemCollector problems )
156     {
157         if ( settingsSource == null )
158         {
159             return new Settings();
160         }
161 
162         problems.setSource( settingsSource.getLocation() );
163 
164         Settings settings;
165 
166         try
167         {
168             Map<String, ?> options = Collections.singletonMap( SettingsReader.IS_STRICT, Boolean.TRUE );
169 
170             try
171             {
172                 settings = settingsReader.read( settingsSource.getInputStream(), options );
173             }
174             catch ( SettingsParseException e )
175             {
176                 options = Collections.singletonMap( SettingsReader.IS_STRICT, Boolean.FALSE );
177 
178                 settings = settingsReader.read( settingsSource.getInputStream(), options );
179 
180                 problems.add( SettingsProblem.Severity.WARNING, e.getMessage(), e.getLineNumber(), e.getColumnNumber(),
181                               e );
182             }
183         }
184         catch ( SettingsParseException e )
185         {
186             problems.add( SettingsProblem.Severity.FATAL, "Non-parseable settings " + settingsSource.getLocation()
187                 + ": " + e.getMessage(), e.getLineNumber(), e.getColumnNumber(), e );
188             return new Settings();
189         }
190         catch ( IOException e )
191         {
192             problems.add( SettingsProblem.Severity.FATAL, "Non-readable settings " + settingsSource.getLocation()
193                 + ": " + e.getMessage(), -1, -1, e );
194             return new Settings();
195         }
196 
197         settingsValidator.validate( settings, problems );
198 
199         return settings;
200     }
201 
202     private Settings interpolate( Settings settings, SettingsBuildingRequest request,
203                                   SettingsProblemCollector problems )
204     {
205         StringWriter writer = new StringWriter( 1024 * 4 );
206 
207         try
208         {
209             settingsWriter.write( writer, null, settings );
210         }
211         catch ( IOException e )
212         {
213             throw new IllegalStateException( "Failed to serialize settings to memory", e );
214         }
215 
216         String serializedSettings = writer.toString();
217 
218         RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
219 
220         interpolator.addValueSource( new PropertiesBasedValueSource( request.getUserProperties() ) );
221 
222         interpolator.addValueSource( new PropertiesBasedValueSource( request.getSystemProperties() ) );
223 
224         try
225         {
226             interpolator.addValueSource( new EnvarBasedValueSource() );
227         }
228         catch ( IOException e )
229         {
230             problems.add( SettingsProblem.Severity.WARNING, "Failed to use environment variables for interpolation: "
231                 + e.getMessage(), -1, -1, e );
232         }
233 
234         interpolator.addPostProcessor( new InterpolationPostProcessor()
235         {
236             @Override
237             public Object execute( String expression, Object value )
238             {
239                 if ( value != null )
240                 {
241                     // we're going to parse this back in as XML so we need to escape XML markup
242                     value = value.toString().replace( "&", "&amp;" ).replace( "<", "&lt;" ).replace( ">", "&gt;" );
243                     return value;
244                 }
245                 return null;
246             }
247         } );
248 
249         try
250         {
251             serializedSettings = interpolator.interpolate( serializedSettings, "settings" );
252         }
253         catch ( InterpolationException e )
254         {
255             problems.add( SettingsProblem.Severity.ERROR, "Failed to interpolate settings: " + e.getMessage(), -1, -1,
256                           e );
257 
258             return settings;
259         }
260 
261         Settings result;
262         try
263         {
264             Map<String, ?> options = Collections.singletonMap( SettingsReader.IS_STRICT, Boolean.FALSE );
265             result = settingsReader.read( new StringReader( serializedSettings ), options );
266         }
267         catch ( IOException e )
268         {
269             problems.add( SettingsProblem.Severity.ERROR, "Failed to interpolate settings: " + e.getMessage(), -1, -1,
270                           e );
271             return settings;
272         }
273 
274         return result;
275     }
276 
277 }