1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.internal.impl;
20
21 import javax.xml.stream.Location;
22 import javax.xml.stream.XMLStreamException;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.util.ArrayList;
30 import java.util.List;
31
32 import org.apache.maven.api.di.Named;
33 import org.apache.maven.api.services.BuilderProblem;
34 import org.apache.maven.api.services.SettingsBuilder;
35 import org.apache.maven.api.services.SettingsBuilderException;
36 import org.apache.maven.api.services.SettingsBuilderRequest;
37 import org.apache.maven.api.services.SettingsBuilderResult;
38 import org.apache.maven.api.services.Source;
39 import org.apache.maven.api.services.xml.SettingsXmlFactory;
40 import org.apache.maven.api.services.xml.XmlReaderException;
41 import org.apache.maven.api.services.xml.XmlReaderRequest;
42 import org.apache.maven.api.settings.Profile;
43 import org.apache.maven.api.settings.Repository;
44 import org.apache.maven.api.settings.RepositoryPolicy;
45 import org.apache.maven.api.settings.Server;
46 import org.apache.maven.api.settings.Settings;
47 import org.apache.maven.settings.v4.SettingsMerger;
48 import org.apache.maven.settings.v4.SettingsTransformer;
49 import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
50 import org.codehaus.plexus.interpolation.InterpolationException;
51 import org.codehaus.plexus.interpolation.MapBasedValueSource;
52 import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
53
54
55
56
57
58 @Named
59 public class DefaultSettingsBuilder implements SettingsBuilder {
60
61 private final DefaultSettingsValidator settingsValidator = new DefaultSettingsValidator();
62
63 private final SettingsMerger settingsMerger = new SettingsMerger();
64
65 @Override
66 public SettingsBuilderResult build(SettingsBuilderRequest request) throws SettingsBuilderException {
67 List<BuilderProblem> problems = new ArrayList<>();
68
69 Source installationSource = request.getInstallationSettingsSource().orElse(null);
70 Settings installation = readSettings(installationSource, false, request, problems);
71
72 Source projectSource = request.getProjectSettingsSource().orElse(null);
73 Settings project = readSettings(projectSource, true, request, problems);
74
75 Source userSource = request.getUserSettingsSource().orElse(null);
76 Settings user = readSettings(userSource, false, request, problems);
77
78 Settings effective =
79 settingsMerger.merge(user, settingsMerger.merge(project, installation, false, null), false, null);
80
81
82
83
84 if (effective.getRepositories().isEmpty()
85 && effective.getPluginRepositories().isEmpty()) {
86 Repository central = Repository.newBuilder()
87 .id("central")
88 .name("Central Repository")
89 .url("https://repo.maven.apache.org/maven2")
90 .snapshots(RepositoryPolicy.newBuilder().enabled(false).build())
91 .build();
92 Repository centralWithNoUpdate = central.withReleases(
93 RepositoryPolicy.newBuilder().updatePolicy("never").build());
94 effective = Settings.newBuilder(effective)
95 .repositories(List.of(central))
96 .pluginRepositories(List.of(centralWithNoUpdate))
97 .build();
98 }
99
100
101 String localRepository = effective.getLocalRepository();
102 if (localRepository != null && !localRepository.isEmpty()) {
103 Path file = Paths.get(localRepository);
104 if (!file.isAbsolute() && file.toString().startsWith(File.separator)) {
105 effective = effective.withLocalRepository(file.toAbsolutePath().toString());
106 }
107 }
108
109 if (hasErrors(problems)) {
110 throw new SettingsBuilderException("Error building settings", problems);
111 }
112
113 return new DefaultSettingsBuilderResult(effective, problems);
114 }
115
116 private boolean hasErrors(List<BuilderProblem> problems) {
117 if (problems != null) {
118 for (BuilderProblem problem : problems) {
119 if (BuilderProblem.Severity.ERROR.compareTo(problem.getSeverity()) >= 0) {
120 return true;
121 }
122 }
123 }
124
125 return false;
126 }
127
128 private Settings readSettings(
129 Source settingsSource,
130 boolean isProjectSettings,
131 SettingsBuilderRequest request,
132 List<BuilderProblem> problems) {
133 if (settingsSource == null) {
134 return Settings.newInstance();
135 }
136
137 Settings settings;
138
139 try {
140 try (InputStream is = settingsSource.openStream()) {
141 settings = request.getSession()
142 .getService(SettingsXmlFactory.class)
143 .read(XmlReaderRequest.builder()
144 .inputStream(is)
145 .location(settingsSource.getLocation())
146 .strict(true)
147 .build());
148 } catch (XmlReaderException e) {
149 try (InputStream is = settingsSource.openStream()) {
150 settings = request.getSession()
151 .getService(SettingsXmlFactory.class)
152 .read(XmlReaderRequest.builder()
153 .inputStream(is)
154 .location(settingsSource.getLocation())
155 .strict(false)
156 .build());
157 Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
158 problems.add(new DefaultBuilderProblem(
159 settingsSource.getLocation(),
160 loc != null ? loc.getLineNumber() : -1,
161 loc != null ? loc.getColumnNumber() : -1,
162 e,
163 e.getMessage(),
164 BuilderProblem.Severity.WARNING));
165 }
166 }
167 } catch (XmlReaderException e) {
168 Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
169 problems.add(new DefaultBuilderProblem(
170 settingsSource.getLocation(),
171 loc != null ? loc.getLineNumber() : -1,
172 loc != null ? loc.getColumnNumber() : -1,
173 e,
174 "Non-parseable settings " + settingsSource.getLocation() + ": " + e.getMessage(),
175 BuilderProblem.Severity.FATAL));
176 return Settings.newInstance();
177 } catch (IOException e) {
178 problems.add(new DefaultBuilderProblem(
179 settingsSource.getLocation(),
180 -1,
181 -1,
182 e,
183 "Non-readable settings " + settingsSource.getLocation() + ": " + e.getMessage(),
184 BuilderProblem.Severity.FATAL));
185 return Settings.newInstance();
186 }
187
188 settings = interpolate(settings, request, problems);
189
190 settingsValidator.validate(settings, isProjectSettings, problems);
191
192 if (isProjectSettings) {
193 settings = Settings.newBuilder(settings, true)
194 .localRepository(null)
195 .interactiveMode(false)
196 .offline(false)
197 .proxies(List.of())
198 .usePluginRegistry(false)
199 .servers(settings.getServers().stream()
200 .map(s -> Server.newBuilder(s, true)
201 .username(null)
202 .passphrase(null)
203 .privateKey(null)
204 .password(null)
205 .filePermissions(null)
206 .directoryPermissions(null)
207 .build())
208 .toList())
209 .build();
210 }
211
212 return settings;
213 }
214
215 private Settings interpolate(Settings settings, SettingsBuilderRequest request, List<BuilderProblem> problems) {
216
217 RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
218
219 interpolator.addValueSource(new MapBasedValueSource(request.getSession().getUserProperties()));
220
221 interpolator.addValueSource(new MapBasedValueSource(request.getSession().getSystemProperties()));
222
223 try {
224 interpolator.addValueSource(new EnvarBasedValueSource());
225 } catch (IOException e) {
226 problems.add(new DefaultBuilderProblem(
227 null,
228 -1,
229 -1,
230 e,
231 "Failed to use environment variables for interpolation: " + e.getMessage(),
232 BuilderProblem.Severity.WARNING));
233 }
234
235 return new SettingsTransformer(value -> {
236 try {
237 return value != null ? interpolator.interpolate(value) : null;
238 } catch (InterpolationException e) {
239 problems.add(new DefaultBuilderProblem(
240 null,
241 -1,
242 -1,
243 e,
244 "Failed to interpolate settings: " + e.getMessage(),
245 BuilderProblem.Severity.WARNING));
246 return value;
247 }
248 })
249 .visit(settings);
250 }
251
252 @Override
253 public List<BuilderProblem> validate(Settings settings, boolean isProjectSettings) {
254 ArrayList<BuilderProblem> problems = new ArrayList<>();
255 settingsValidator.validate(settings, isProjectSettings, problems);
256 return problems;
257 }
258
259 @Override
260 public Profile convert(org.apache.maven.api.model.Profile profile) {
261 return SettingsUtilsV4.convertToSettingsProfile(profile);
262 }
263
264 @Override
265 public org.apache.maven.api.model.Profile convert(Profile profile) {
266 return SettingsUtilsV4.convertFromSettingsProfile(profile);
267 }
268
269
270
271
272
273 static class DefaultSettingsBuilderResult implements SettingsBuilderResult {
274
275 private final Settings effectiveSettings;
276
277 private final List<BuilderProblem> problems;
278
279 DefaultSettingsBuilderResult(Settings effectiveSettings, List<BuilderProblem> problems) {
280 this.effectiveSettings = effectiveSettings;
281 this.problems = (problems != null) ? problems : new ArrayList<>();
282 }
283
284 @Override
285 public Settings getEffectiveSettings() {
286 return effectiveSettings;
287 }
288
289 @Override
290 public List<BuilderProblem> getProblems() {
291 return problems;
292 }
293 }
294 }