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