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          Settings globalSettings = readSettings( request.getGlobalSettingsFile(), request, problems );
90  
91          Settings userSettings = readSettings( request.getUserSettingsFile(), request, problems );
92  
93          settingsMerger.merge( userSettings, globalSettings, TrackableBase.GLOBAL_LEVEL );
94  
95          problems.setSource( "" );
96  
97          userSettings = interpolate( userSettings, request, problems );
98  
99          // for the special case of a drive-relative Windows path, make sure it's absolute to save plugins from trouble
100         String localRepository = userSettings.getLocalRepository();
101         if ( localRepository != null && localRepository.length() > 0 )
102         {
103             File file = new File( localRepository );
104             if ( !file.isAbsolute() && file.getPath().startsWith( File.separator ) )
105             {
106                 userSettings.setLocalRepository( file.getAbsolutePath() );
107             }
108         }
109 
110         if ( hasErrors( problems.getProblems() ) )
111         {
112             throw new SettingsBuildingException( problems.getProblems() );
113         }
114 
115         return new DefaultSettingsBuildingResult( userSettings, problems.getProblems() );
116     }
117 
118     private boolean hasErrors( List<SettingsProblem> problems )
119     {
120         if ( problems != null )
121         {
122             for ( SettingsProblem problem : problems )
123             {
124                 if ( SettingsProblem.Severity.ERROR.compareTo( problem.getSeverity() ) >= 0 )
125                 {
126                     return true;
127                 }
128             }
129         }
130 
131         return false;
132     }
133 
134     private Settings readSettings( File settingsFile, SettingsBuildingRequest request,
135                                    DefaultSettingsProblemCollector problems )
136     {
137         if ( settingsFile == null || !settingsFile.exists() )
138         {
139             return new Settings();
140         }
141 
142         problems.setSource( settingsFile.getAbsolutePath() );
143 
144         Settings settings;
145 
146         try
147         {
148             Map<String, ?> options = Collections.singletonMap( SettingsReader.IS_STRICT, Boolean.TRUE );
149 
150             try
151             {
152                 settings = settingsReader.read( settingsFile, options );
153             }
154             catch ( SettingsParseException e )
155             {
156                 options = Collections.singletonMap( SettingsReader.IS_STRICT, Boolean.FALSE );
157 
158                 settings = settingsReader.read( settingsFile, options );
159 
160                 problems.add( SettingsProblem.Severity.WARNING, e.getMessage(), e.getLineNumber(), e.getColumnNumber(),
161                               e );
162             }
163         }
164         catch ( SettingsParseException e )
165         {
166             problems.add( SettingsProblem.Severity.FATAL, "Non-parseable settings " + settingsFile + ": "
167                 + e.getMessage(), e.getLineNumber(), e.getColumnNumber(), e );
168             return new Settings();
169         }
170         catch ( IOException e )
171         {
172             problems.add( SettingsProblem.Severity.FATAL, "Non-readable settings " + settingsFile + ": "
173                 + e.getMessage(), -1, -1, e );
174             return new Settings();
175         }
176 
177         settingsValidator.validate( settings, problems );
178 
179         return settings;
180     }
181 
182     private Settings interpolate( Settings settings, SettingsBuildingRequest request, SettingsProblemCollector problems )
183     {
184         StringWriter writer = new StringWriter( 1024 * 4 );
185 
186         try
187         {
188             settingsWriter.write( writer, null, settings );
189         }
190         catch ( IOException e )
191         {
192             throw new IllegalStateException( "Failed to serialize settings to memory", e );
193         }
194 
195         String serializedSettings = writer.toString();
196 
197         RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
198 
199         interpolator.addValueSource( new PropertiesBasedValueSource( request.getUserProperties() ) );
200 
201         interpolator.addValueSource( new PropertiesBasedValueSource( request.getSystemProperties() ) );
202 
203         try
204         {
205             interpolator.addValueSource( new EnvarBasedValueSource() );
206         }
207         catch ( IOException e )
208         {
209             problems.add( SettingsProblem.Severity.WARNING, "Failed to use environment variables for interpolation: "
210                 + e.getMessage(), -1, -1, e );
211         }
212 
213         interpolator.addPostProcessor( new InterpolationPostProcessor()
214         {
215             public Object execute( String expression, Object value )
216             {
217                 if ( value != null )
218                 {
219                     // we're going to parse this back in as XML so we need to escape XML markup
220                     value = value.toString().replace( "&", "&amp;" ).replace( "<", "&lt;" ).replace( ">", "&gt;" );
221                     return value;
222                 }
223                 return null;
224             }
225         } );
226 
227         try
228         {
229             serializedSettings = interpolator.interpolate( serializedSettings, "settings" );
230         }
231         catch ( InterpolationException e )
232         {
233             problems.add( SettingsProblem.Severity.ERROR, "Failed to interpolate settings: " + e.getMessage(), -1, -1,
234                           e );
235 
236             return settings;
237         }
238 
239         Settings result;
240         try
241         {
242             Map<String, ?> options = Collections.singletonMap( SettingsReader.IS_STRICT, Boolean.FALSE );
243             result = settingsReader.read( new StringReader( serializedSettings ), options );
244         }
245         catch ( IOException e )
246         {
247             problems.add( SettingsProblem.Severity.ERROR, "Failed to interpolate settings: " + e.getMessage(), -1, -1,
248                           e );
249             return settings;
250         }
251 
252         return result;
253     }
254 
255 }