001package org.apache.maven.settings.building; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.File; 023import java.io.IOException; 024import java.io.StringReader; 025import java.io.StringWriter; 026import java.util.Collections; 027import java.util.List; 028import java.util.Map; 029 030import org.apache.maven.settings.Settings; 031import org.apache.maven.settings.TrackableBase; 032import org.apache.maven.settings.io.SettingsParseException; 033import org.apache.maven.settings.io.SettingsReader; 034import org.apache.maven.settings.io.SettingsWriter; 035import org.apache.maven.settings.merge.MavenSettingsMerger; 036import org.apache.maven.settings.validation.SettingsValidator; 037import org.codehaus.plexus.component.annotations.Component; 038import org.codehaus.plexus.component.annotations.Requirement; 039import org.codehaus.plexus.interpolation.EnvarBasedValueSource; 040import org.codehaus.plexus.interpolation.InterpolationException; 041import org.codehaus.plexus.interpolation.InterpolationPostProcessor; 042import org.codehaus.plexus.interpolation.PropertiesBasedValueSource; 043import org.codehaus.plexus.interpolation.RegexBasedInterpolator; 044 045/** 046 * Builds the effective settings from a user settings file and/or a global settings file. 047 * 048 * @author Benjamin Bentmann 049 */ 050@Component( role = SettingsBuilder.class ) 051public class DefaultSettingsBuilder 052 implements SettingsBuilder 053{ 054 055 @Requirement 056 private SettingsReader settingsReader; 057 058 @Requirement 059 private SettingsWriter settingsWriter; 060 061 @Requirement 062 private SettingsValidator settingsValidator; 063 064 private MavenSettingsMerger settingsMerger = new MavenSettingsMerger(); 065 066 public DefaultSettingsBuilder setSettingsReader( SettingsReader settingsReader ) 067 { 068 this.settingsReader = settingsReader; 069 return this; 070 } 071 072 public DefaultSettingsBuilder setSettingsWriter( SettingsWriter settingsWriter ) 073 { 074 this.settingsWriter = settingsWriter; 075 return this; 076 } 077 078 public DefaultSettingsBuilder setSettingsValidator( SettingsValidator settingsValidator ) 079 { 080 this.settingsValidator = settingsValidator; 081 return this; 082 } 083 084 public SettingsBuildingResult build( SettingsBuildingRequest request ) 085 throws SettingsBuildingException 086 { 087 DefaultSettingsProblemCollector problems = new DefaultSettingsProblemCollector( null ); 088 089 SettingsSource globalSettingsSource = 090 getSettingsSource( request.getGlobalSettingsFile(), request.getGlobalSettingsSource() ); 091 Settings globalSettings = readSettings( globalSettingsSource, request, problems ); 092 093 SettingsSource userSettingsSource = 094 getSettingsSource( request.getUserSettingsFile(), request.getUserSettingsSource() ); 095 Settings userSettings = readSettings( userSettingsSource, request, problems ); 096 097 settingsMerger.merge( userSettings, globalSettings, TrackableBase.GLOBAL_LEVEL ); 098 099 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, 200 SettingsProblemCollector problems ) 201 { 202 StringWriter writer = new StringWriter( 1024 * 4 ); 203 204 try 205 { 206 settingsWriter.write( writer, null, settings ); 207 } 208 catch ( IOException e ) 209 { 210 throw new IllegalStateException( "Failed to serialize settings to memory", e ); 211 } 212 213 String serializedSettings = writer.toString(); 214 215 RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); 216 217 interpolator.addValueSource( new PropertiesBasedValueSource( request.getUserProperties() ) ); 218 219 interpolator.addValueSource( new PropertiesBasedValueSource( request.getSystemProperties() ) ); 220 221 try 222 { 223 interpolator.addValueSource( new EnvarBasedValueSource() ); 224 } 225 catch ( IOException e ) 226 { 227 problems.add( SettingsProblem.Severity.WARNING, "Failed to use environment variables for interpolation: " 228 + e.getMessage(), -1, -1, e ); 229 } 230 231 interpolator.addPostProcessor( new InterpolationPostProcessor() 232 { 233 public Object execute( String expression, Object value ) 234 { 235 if ( value != null ) 236 { 237 // we're going to parse this back in as XML so we need to escape XML markup 238 value = value.toString().replace( "&", "&" ).replace( "<", "<" ).replace( ">", ">" ); 239 return value; 240 } 241 return null; 242 } 243 } ); 244 245 try 246 { 247 serializedSettings = interpolator.interpolate( serializedSettings, "settings" ); 248 } 249 catch ( InterpolationException e ) 250 { 251 problems.add( SettingsProblem.Severity.ERROR, "Failed to interpolate settings: " + e.getMessage(), -1, -1, 252 e ); 253 254 return settings; 255 } 256 257 Settings result; 258 try 259 { 260 Map<String, ?> options = Collections.singletonMap( SettingsReader.IS_STRICT, Boolean.FALSE ); 261 result = settingsReader.read( new StringReader( serializedSettings ), options ); 262 } 263 catch ( IOException e ) 264 { 265 problems.add( SettingsProblem.Severity.ERROR, "Failed to interpolate settings: " + e.getMessage(), -1, -1, 266 e ); 267 return settings; 268 } 269 270 return result; 271 } 272 273}