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