1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.settings.building;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.StringReader;
29 import java.nio.charset.StandardCharsets;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.stream.Collectors;
35 import java.util.stream.Stream;
36
37 import org.apache.maven.building.FileSource;
38 import org.apache.maven.building.Source;
39 import org.apache.maven.settings.Server;
40 import org.apache.maven.settings.Settings;
41 import org.apache.maven.settings.TrackableBase;
42 import org.apache.maven.settings.io.SettingsParseException;
43 import org.apache.maven.settings.io.SettingsReader;
44 import org.apache.maven.settings.io.SettingsWriter;
45 import org.apache.maven.settings.merge.MavenSettingsMerger;
46 import org.apache.maven.settings.validation.SettingsValidator;
47 import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
48 import org.codehaus.plexus.interpolation.InterpolationException;
49 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
50 import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
51 import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
52 import org.codehaus.plexus.util.IOUtil;
53
54
55
56
57
58
59 @Named
60 @Singleton
61 public class DefaultSettingsBuilder implements SettingsBuilder {
62 private static final String MAVEN_SETTINGS_STRICT = "maven.settings.strictParsing";
63
64 private SettingsReader settingsReader;
65
66 private SettingsWriter settingsWriter;
67
68 private SettingsValidator settingsValidator;
69
70 private final MavenSettingsMerger settingsMerger = new MavenSettingsMerger();
71
72 @Inject
73 public DefaultSettingsBuilder(
74 SettingsReader settingsReader, SettingsWriter settingsWriter, SettingsValidator settingsValidator) {
75 this.settingsReader = settingsReader;
76 this.settingsWriter = settingsWriter;
77 this.settingsValidator = settingsValidator;
78 }
79
80 public DefaultSettingsBuilder setSettingsReader(SettingsReader settingsReader) {
81 this.settingsReader = settingsReader;
82 return this;
83 }
84
85 public DefaultSettingsBuilder setSettingsWriter(SettingsWriter settingsWriter) {
86 this.settingsWriter = settingsWriter;
87 return this;
88 }
89
90 public DefaultSettingsBuilder setSettingsValidator(SettingsValidator settingsValidator) {
91 this.settingsValidator = settingsValidator;
92 return this;
93 }
94
95 @Override
96 public SettingsBuildingResult build(SettingsBuildingRequest request) throws SettingsBuildingException {
97 DefaultSettingsProblemCollector problems = new DefaultSettingsProblemCollector(null);
98
99 boolean strict = Boolean.parseBoolean(request.getUserProperties()
100 .getProperty(
101 MAVEN_SETTINGS_STRICT,
102 request.getSystemProperties().getProperty(MAVEN_SETTINGS_STRICT, Boolean.FALSE.toString())));
103
104 Source globalSettingsSource =
105 getSettingsSource(request.getGlobalSettingsFile(), request.getGlobalSettingsSource());
106 Settings globalSettings = readSettingsFromString(
107 globalSettingsSource,
108 interpolateFromSourceToString(globalSettingsSource, request, problems),
109 request,
110 problems,
111 strict);
112
113 Source userSettingsSource = getSettingsSource(request.getUserSettingsFile(), request.getUserSettingsSource());
114 Settings userSettings = readSettingsFromString(
115 userSettingsSource,
116 interpolateFromSourceToString(userSettingsSource, request, problems),
117 request,
118 problems,
119 strict);
120
121 settingsMerger.merge(userSettings, globalSettings, TrackableBase.GLOBAL_LEVEL);
122
123 problems.setSource("");
124
125
126 String localRepository = userSettings.getLocalRepository();
127 if (localRepository != null && !localRepository.isEmpty()) {
128 File file = new File(localRepository);
129 if (!file.isAbsolute() && file.getPath().startsWith(File.separator)) {
130 userSettings.setLocalRepository(file.getAbsolutePath());
131 }
132 }
133
134 if (hasErrors(problems.getProblems())) {
135 throw new SettingsBuildingException(problems.getProblems());
136 }
137
138 return new DefaultSettingsBuildingResult(userSettings, problems.getProblems());
139 }
140
141 private boolean hasErrors(List<SettingsProblem> problems) {
142 if (problems != null) {
143 for (SettingsProblem problem : problems) {
144 if (SettingsProblem.Severity.ERROR.compareTo(problem.getSeverity()) >= 0) {
145 return true;
146 }
147 }
148 }
149
150 return false;
151 }
152
153 private Source getSettingsSource(File settingsFile, Source settingsSource) {
154 if (settingsSource != null) {
155 return settingsSource;
156 } else if (settingsFile != null && settingsFile.exists()) {
157 return new FileSource(settingsFile);
158 }
159 return null;
160 }
161
162 private Settings readSettingsFromString(
163 Source settingsSource,
164 String settingsString,
165 SettingsBuildingRequest request,
166 DefaultSettingsProblemCollector problems,
167 boolean strict) {
168 if (settingsSource == null) {
169 return new Settings();
170 }
171
172 problems.setSource(settingsSource.getLocation());
173
174 Settings settings;
175
176 try {
177 Map<String, ?> options = Collections.singletonMap(SettingsReader.IS_STRICT, Boolean.TRUE);
178
179 try {
180 settings = settingsReader.read(new StringReader(settingsString), options);
181 } catch (SettingsParseException e) {
182 if (strict) {
183 problems.add(
184 SettingsProblem.Severity.FATAL, e.getMessage(), e.getLineNumber(), e.getColumnNumber(), e);
185 return new Settings();
186 } else {
187 options = Collections.singletonMap(SettingsReader.IS_STRICT, Boolean.FALSE);
188 settings = settingsReader.read(new StringReader(settingsString), options);
189 problems.add(
190 SettingsProblem.Severity.WARNING,
191 e.getMessage(),
192 e.getLineNumber(),
193 e.getColumnNumber(),
194 e);
195 }
196 }
197 } catch (SettingsParseException e) {
198 problems.add(
199 SettingsProblem.Severity.FATAL,
200 "Non-parseable settings " + settingsSource.getLocation() + ": " + e.getMessage(),
201 e.getLineNumber(),
202 e.getColumnNumber(),
203 e);
204 return new Settings();
205 } catch (IOException e) {
206 problems.add(
207 SettingsProblem.Severity.FATAL,
208 "Non-readable settings " + settingsSource.getLocation() + ": " + e.getMessage(),
209 -1,
210 -1,
211 e);
212 return new Settings();
213 }
214
215 settingsValidator.validate(settings, problems);
216 settings.setServers(new ArrayList<>(serversByIds(settings.getServers())));
217
218 return settings;
219 }
220
221 private String interpolateFromSourceToString(
222 Source settingsSource, SettingsBuildingRequest request, SettingsProblemCollector problems) {
223 String serializedSettings = null;
224 if (settingsSource == null) {
225 return "<settings></settings>";
226 }
227 try {
228 try (InputStream inputStream = settingsSource.getInputStream()) {
229 serializedSettings = IOUtil.toString(inputStream, StandardCharsets.UTF_8.name());
230 }
231 RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
232 interpolator.addValueSource(new PropertiesBasedValueSource(request.getUserProperties()));
233 interpolator.addValueSource(new PropertiesBasedValueSource(request.getSystemProperties()));
234
235 try {
236 interpolator.addValueSource(new EnvarBasedValueSource());
237 } catch (IOException e) {
238 problems.add(
239 SettingsProblem.Severity.WARNING,
240 "Failed to use environment variables for interpolation: " + e.getMessage(),
241 -1,
242 -1,
243 e);
244 }
245
246 interpolator.addPostProcessor(new InterpolationPostProcessor() {
247 @Override
248 public Object execute(String expression, Object value) {
249 if (value != null) {
250
251 value = value.toString()
252 .replace("&", "&")
253 .replace("<", "<")
254 .replace(">", ">");
255 return value;
256 }
257 return null;
258 }
259 });
260
261 try {
262 serializedSettings = interpolator.interpolate(serializedSettings, "settings");
263 } catch (InterpolationException e) {
264 problems.add(
265 SettingsProblem.Severity.ERROR, "Failed to interpolate settings: " + e.getMessage(), -1, -1, e);
266
267 return serializedSettings;
268 }
269
270 return serializedSettings;
271 } catch (IOException e) {
272 problems.add(
273 SettingsProblem.Severity.ERROR, "Failed to interpolate settings: " + e.getMessage(), -1, -1, e);
274 return serializedSettings;
275 }
276 }
277
278 private List<Server> serversByIds(List<Server> servers) {
279 return servers.stream()
280 .flatMap(server -> Stream.concat(
281 Stream.of(server), server.getAliases().stream().map(id -> serverAlias(server, id))))
282 .collect(Collectors.toList());
283 }
284
285 private Server serverAlias(Server server, String id) {
286 Server alias = server.clone();
287 alias.setId(id);
288 alias.setAliases(Collections.emptyList());
289 return alias;
290 }
291 }