View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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.util.Collections;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.apache.maven.api.settings.InputSource;
33  import org.apache.maven.building.FileSource;
34  import org.apache.maven.building.Source;
35  import org.apache.maven.settings.Repository;
36  import org.apache.maven.settings.RepositoryPolicy;
37  import org.apache.maven.settings.Server;
38  import org.apache.maven.settings.Settings;
39  import org.apache.maven.settings.TrackableBase;
40  import org.apache.maven.settings.io.SettingsParseException;
41  import org.apache.maven.settings.io.SettingsReader;
42  import org.apache.maven.settings.io.SettingsWriter;
43  import org.apache.maven.settings.merge.MavenSettingsMerger;
44  import org.apache.maven.settings.v4.SettingsTransformer;
45  import org.apache.maven.settings.validation.SettingsValidator;
46  import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
47  import org.codehaus.plexus.interpolation.InterpolationException;
48  import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
49  import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
50  
51  /**
52   * Builds the effective settings from a user settings file and/or a global settings file.
53   *
54   */
55  @Named
56  @Singleton
57  public class DefaultSettingsBuilder implements SettingsBuilder {
58  
59      private SettingsReader settingsReader;
60  
61      private SettingsWriter settingsWriter;
62  
63      private SettingsValidator settingsValidator;
64  
65      private final MavenSettingsMerger settingsMerger = new MavenSettingsMerger();
66  
67      @Inject
68      public DefaultSettingsBuilder(
69              SettingsReader settingsReader, SettingsWriter settingsWriter, SettingsValidator settingsValidator) {
70          this.settingsReader = settingsReader;
71          this.settingsWriter = settingsWriter;
72          this.settingsValidator = settingsValidator;
73      }
74  
75      public DefaultSettingsBuilder setSettingsReader(SettingsReader settingsReader) {
76          this.settingsReader = settingsReader;
77          return this;
78      }
79  
80      public DefaultSettingsBuilder setSettingsWriter(SettingsWriter settingsWriter) {
81          this.settingsWriter = settingsWriter;
82          return this;
83      }
84  
85      public DefaultSettingsBuilder setSettingsValidator(SettingsValidator settingsValidator) {
86          this.settingsValidator = settingsValidator;
87          return this;
88      }
89  
90      @Override
91      public SettingsBuildingResult build(SettingsBuildingRequest request) throws SettingsBuildingException {
92          DefaultSettingsProblemCollector problems = new DefaultSettingsProblemCollector(null);
93  
94          Source globalSettingsSource =
95                  getSettingsSource(request.getGlobalSettingsFile(), request.getGlobalSettingsSource());
96          Settings globalSettings = readSettings(globalSettingsSource, false, request, problems);
97  
98          Source projectSettingsSource =
99                  getSettingsSource(request.getProjectSettingsFile(), request.getProjectSettingsSource());
100         Settings projectSettings = readSettings(projectSettingsSource, true, request, problems);
101 
102         Source userSettingsSource = getSettingsSource(request.getUserSettingsFile(), request.getUserSettingsSource());
103         Settings userSettings = readSettings(userSettingsSource, false, request, problems);
104 
105         settingsMerger.merge(projectSettings, globalSettings, TrackableBase.GLOBAL_LEVEL);
106         settingsMerger.merge(userSettings, projectSettings, TrackableBase.PROJECT_LEVEL);
107 
108         // If no repository is defined in the user/global settings,
109         // it means that we have "old" settings (as those are new in 4.0)
110         // so add central to the computed settings for backward compatibility.
111         if (userSettings.getRepositories().isEmpty()
112                 && userSettings.getPluginRepositories().isEmpty()) {
113             Repository central = new Repository();
114             central.setId("central");
115             central.setName("Central Repository");
116             central.setUrl("https://repo.maven.apache.org/maven2");
117             RepositoryPolicy disabledPolicy = new RepositoryPolicy();
118             disabledPolicy.setEnabled(false);
119             central.setSnapshots(disabledPolicy);
120             userSettings.getRepositories().add(central);
121             central = central.clone();
122             RepositoryPolicy updateNeverPolicy = new RepositoryPolicy();
123             disabledPolicy.setUpdatePolicy("never");
124             central.setReleases(updateNeverPolicy);
125             userSettings.getPluginRepositories().add(central);
126         }
127 
128         problems.setSource("");
129 
130         // for the special case of a drive-relative Windows path, make sure it's absolute to save plugins from trouble
131         String localRepository = userSettings.getLocalRepository();
132         if (localRepository != null && localRepository.length() > 0) {
133             File file = new File(localRepository);
134             if (!file.isAbsolute() && file.getPath().startsWith(File.separator)) {
135                 userSettings.setLocalRepository(file.getAbsolutePath());
136             }
137         }
138 
139         if (hasErrors(problems.getProblems())) {
140             throw new SettingsBuildingException(problems.getProblems());
141         }
142 
143         return new DefaultSettingsBuildingResult(userSettings, problems.getProblems());
144     }
145 
146     private boolean hasErrors(List<SettingsProblem> problems) {
147         if (problems != null) {
148             for (SettingsProblem problem : problems) {
149                 if (SettingsProblem.Severity.ERROR.compareTo(problem.getSeverity()) >= 0) {
150                     return true;
151                 }
152             }
153         }
154 
155         return false;
156     }
157 
158     private Source getSettingsSource(File settingsFile, Source settingsSource) {
159         if (settingsSource != null) {
160             return settingsSource;
161         } else if (settingsFile != null && settingsFile.exists()) {
162             return new FileSource(settingsFile);
163         }
164         return null;
165     }
166 
167     private Settings readSettings(
168             Source settingsSource,
169             boolean isProjectSettings,
170             SettingsBuildingRequest request,
171             DefaultSettingsProblemCollector problems) {
172         if (settingsSource == null) {
173             return new Settings();
174         }
175 
176         problems.setSource(settingsSource.getLocation());
177 
178         Settings settings;
179 
180         try {
181             Map<String, Object> options = new HashMap<>();
182             options.put(SettingsReader.IS_STRICT, Boolean.TRUE);
183             options.put(InputSource.class.getName(), new InputSource(settingsSource.getLocation()));
184             try {
185                 settings = settingsReader.read(settingsSource.getInputStream(), options);
186             } catch (SettingsParseException e) {
187                 options = Collections.singletonMap(SettingsReader.IS_STRICT, Boolean.FALSE);
188 
189                 settings = settingsReader.read(settingsSource.getInputStream(), options);
190 
191                 problems.add(
192                         SettingsProblem.Severity.WARNING, e.getMessage(), e.getLineNumber(), e.getColumnNumber(), e);
193             }
194         } catch (SettingsParseException e) {
195             problems.add(
196                     SettingsProblem.Severity.FATAL,
197                     "Non-parseable settings " + settingsSource.getLocation() + ": " + e.getMessage(),
198                     e.getLineNumber(),
199                     e.getColumnNumber(),
200                     e);
201             return new Settings();
202         } catch (IOException e) {
203             problems.add(
204                     SettingsProblem.Severity.FATAL,
205                     "Non-readable settings " + settingsSource.getLocation() + ": " + e.getMessage(),
206                     -1,
207                     -1,
208                     e);
209             return new Settings();
210         }
211 
212         settings = interpolate(settings, request, problems);
213 
214         settingsValidator.validate(settings, isProjectSettings, problems);
215 
216         if (isProjectSettings) {
217             settings.setLocalRepository(null);
218             settings.setInteractiveMode(true);
219             settings.setOffline(false);
220             settings.setProxies(Collections.emptyList());
221             settings.setUsePluginRegistry(false);
222             for (Server server : settings.getServers()) {
223                 server.setUsername(null);
224                 server.setPassword(null);
225                 server.setPrivateKey(null);
226                 server.setPassword(null);
227                 server.setFilePermissions(null);
228                 server.setDirectoryPermissions(null);
229             }
230         }
231 
232         return settings;
233     }
234 
235     private Settings interpolate(
236             Settings settings, SettingsBuildingRequest request, SettingsProblemCollector problems) {
237 
238         RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
239 
240         interpolator.addValueSource(new PropertiesBasedValueSource(request.getUserProperties()));
241 
242         interpolator.addValueSource(new PropertiesBasedValueSource(request.getSystemProperties()));
243 
244         try {
245             interpolator.addValueSource(new EnvarBasedValueSource());
246         } catch (IOException e) {
247             problems.add(
248                     SettingsProblem.Severity.WARNING,
249                     "Failed to use environment variables for interpolation: " + e.getMessage(),
250                     -1,
251                     -1,
252                     e);
253         }
254 
255         return new Settings(new SettingsTransformer(value -> {
256                     try {
257                         return value != null ? interpolator.interpolate(value) : null;
258                     } catch (InterpolationException e) {
259                         problems.add(
260                                 SettingsProblem.Severity.WARNING,
261                                 "Failed to interpolate settings: " + e.getMessage(),
262                                 -1,
263                                 -1,
264                                 e);
265                         return value;
266                     }
267                 })
268                 .visit(settings.getDelegate()));
269     }
270 }