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.impl;
20  
21  import java.util.HashSet;
22  import java.util.List;
23  import java.util.Set;
24  import java.util.regex.Pattern;
25  
26  import org.apache.maven.api.services.BuilderProblem;
27  import org.apache.maven.api.services.ProblemCollector;
28  import org.apache.maven.api.settings.Mirror;
29  import org.apache.maven.api.settings.Profile;
30  import org.apache.maven.api.settings.Proxy;
31  import org.apache.maven.api.settings.Repository;
32  import org.apache.maven.api.settings.Server;
33  import org.apache.maven.api.settings.Settings;
34  
35  /**
36   */
37  public class DefaultSettingsValidator {
38  
39      private static final String ID = "[\\w.-]+";
40      private static final Pattern ID_REGEX = Pattern.compile(ID);
41  
42      private static final String ILLEGAL_REPO_ID_CHARS = "\\/:\"<>|?*"; // ILLEGAL_FS_CHARS
43  
44      @SuppressWarnings("checkstyle:MethodLength")
45      public void validate(Settings settings, boolean isProjectSettings, ProblemCollector<BuilderProblem> problems) {
46          if (isProjectSettings) {
47              String msgS = "is not supported on project settings.";
48              String msgP = "are not supported on project settings.";
49              if (settings.getLocalRepository() != null
50                      && !settings.getLocalRepository().isEmpty()) {
51                  addViolation(problems, BuilderProblem.Severity.WARNING, "localRepository", null, msgS);
52              }
53              if (!settings.isInteractiveMode()) {
54                  addViolation(problems, BuilderProblem.Severity.WARNING, "interactiveMode", null, msgS);
55              }
56              if (settings.isOffline()) {
57                  addViolation(problems, BuilderProblem.Severity.WARNING, "offline", null, msgS);
58              }
59              if (!settings.getProxies().isEmpty()) {
60                  addViolation(problems, BuilderProblem.Severity.WARNING, "proxies", null, msgP);
61              }
62              if (settings.isUsePluginRegistry()) {
63                  addViolation(problems, BuilderProblem.Severity.WARNING, "usePluginRegistry", null, msgS);
64              }
65              List<Server> servers = settings.getServers();
66              for (int i = 0; i < servers.size(); i++) {
67                  Server server = servers.get(i);
68                  String serverField = "servers.server[" + i + "]";
69                  validateStringEmpty(problems, serverField + ".username", server.getUsername(), msgS);
70                  validateStringEmpty(problems, serverField + ".password", server.getPassword(), msgS);
71                  validateStringEmpty(problems, serverField + ".privateKey", server.getPrivateKey(), msgS);
72                  validateStringEmpty(problems, serverField + ".passphrase", server.getPassphrase(), msgS);
73                  validateStringEmpty(problems, serverField + ".filePermissions", server.getFilePermissions(), msgS);
74                  validateStringEmpty(
75                          problems, serverField + ".directoryPermissions", server.getDirectoryPermissions(), msgS);
76              }
77          }
78  
79          if (settings.isUsePluginRegistry()) {
80              addViolation(
81                      problems,
82                      BuilderProblem.Severity.WARNING,
83                      "usePluginRegistry",
84                      null,
85                      "is deprecated and has no effect.");
86          }
87  
88          List<String> pluginGroups = settings.getPluginGroups();
89  
90          if (pluginGroups != null) {
91              for (int i = 0; i < pluginGroups.size(); i++) {
92                  String pluginGroup = pluginGroups.get(i);
93  
94                  validateStringNotEmpty(problems, "pluginGroups.pluginGroup[" + i + "]", pluginGroup, null);
95  
96                  if (!ID_REGEX.matcher(pluginGroup).matches()) {
97                      addViolation(
98                              problems,
99                              BuilderProblem.Severity.ERROR,
100                             "pluginGroups.pluginGroup[" + i + "]",
101                             null,
102                             "must denote a valid group id and match the pattern " + ID);
103                 }
104             }
105         }
106 
107         List<Server> servers = settings.getServers();
108 
109         if (servers != null) {
110             Set<String> serverIds = new HashSet<>();
111 
112             for (int i = 0; i < servers.size(); i++) {
113                 Server server = servers.get(i);
114 
115                 validateStringNotEmpty(problems, "servers.server[" + i + "].id", server.getId(), null);
116 
117                 if (!serverIds.add(server.getId())) {
118                     addViolation(
119                             problems,
120                             BuilderProblem.Severity.WARNING,
121                             "servers.server.id",
122                             null,
123                             "must be unique but found duplicate server with id " + server.getId());
124                 }
125             }
126         }
127 
128         List<Mirror> mirrors = settings.getMirrors();
129 
130         if (mirrors != null) {
131             for (Mirror mirror : mirrors) {
132                 validateStringNotEmpty(problems, "mirrors.mirror.id", mirror.getId(), mirror.getUrl());
133 
134                 validateBannedCharacters(
135                         problems,
136                         "mirrors.mirror.id",
137                         BuilderProblem.Severity.WARNING,
138                         mirror.getId(),
139                         null,
140                         ILLEGAL_REPO_ID_CHARS);
141 
142                 if ("local".equals(mirror.getId())) {
143                     addViolation(
144                             problems,
145                             BuilderProblem.Severity.WARNING,
146                             "mirrors.mirror.id",
147                             null,
148                             "must not be 'local'"
149                                     + ", this identifier is reserved for the local repository"
150                                     + ", using it for other repositories will corrupt your repository metadata.");
151                 }
152 
153                 validateStringNotEmpty(problems, "mirrors.mirror.url", mirror.getUrl(), mirror.getId());
154 
155                 validateStringNotEmpty(problems, "mirrors.mirror.mirrorOf", mirror.getMirrorOf(), mirror.getId());
156             }
157         }
158 
159         List<Profile> profiles = settings.getProfiles();
160 
161         if (profiles != null) {
162             Set<String> profileIds = new HashSet<>();
163 
164             for (Profile profile : profiles) {
165                 if (!profileIds.add(profile.getId())) {
166                     addViolation(
167                             problems,
168                             BuilderProblem.Severity.WARNING,
169                             "profiles.profile.id",
170                             null,
171                             "must be unique but found duplicate profile with id " + profile.getId());
172                 }
173 
174                 String prefix = "profiles.profile[" + profile.getId() + "].";
175 
176                 validateRepositories(problems, profile.getRepositories(), prefix + "repositories.repository");
177                 validateRepositories(
178                         problems, profile.getPluginRepositories(), prefix + "pluginRepositories.pluginRepository");
179             }
180         }
181 
182         List<Proxy> proxies = settings.getProxies();
183 
184         if (proxies != null) {
185             Set<String> proxyIds = new HashSet<>();
186 
187             for (Proxy proxy : proxies) {
188                 if (!proxyIds.add(proxy.getId())) {
189                     addViolation(
190                             problems,
191                             BuilderProblem.Severity.WARNING,
192                             "proxies.proxy.id",
193                             null,
194                             "must be unique but found duplicate proxy with id " + proxy.getId());
195                 }
196                 validateStringNotEmpty(problems, "proxies.proxy.host", proxy.getHost(), proxy.getId());
197 
198                 try {
199                     Integer.parseInt(proxy.getPortString());
200                 } catch (NumberFormatException e) {
201                     addViolation(
202                             problems,
203                             BuilderProblem.Severity.ERROR,
204                             "proxies.proxy[" + proxy.getId() + "].port",
205                             null,
206                             "must be a valid integer but found '" + proxy.getPortString() + "'");
207                 }
208             }
209         }
210     }
211 
212     private void validateRepositories(
213             ProblemCollector<BuilderProblem> problems, List<Repository> repositories, String prefix) {
214         Set<String> repoIds = new HashSet<>();
215 
216         for (Repository repository : repositories) {
217             validateStringNotEmpty(problems, prefix + ".id", repository.getId(), repository.getUrl());
218 
219             validateBannedCharacters(
220                     problems,
221                     prefix + ".id",
222                     BuilderProblem.Severity.WARNING,
223                     repository.getId(),
224                     null,
225                     ILLEGAL_REPO_ID_CHARS);
226 
227             if ("local".equals(repository.getId())) {
228                 addViolation(
229                         problems,
230                         BuilderProblem.Severity.WARNING,
231                         prefix + ".id",
232                         null,
233                         "must not be 'local'"
234                                 + ", this identifier is reserved for the local repository"
235                                 + ", using it for other repositories will corrupt your repository metadata.");
236             }
237 
238             if (!repoIds.add(repository.getId())) {
239                 addViolation(
240                         problems,
241                         BuilderProblem.Severity.WARNING,
242                         prefix + ".id",
243                         null,
244                         "must be unique but found duplicate repository with id " + repository.getId());
245             }
246 
247             validateStringNotEmpty(problems, prefix + ".url", repository.getUrl(), repository.getId());
248 
249             if ("legacy".equals(repository.getLayout())) {
250                 addViolation(
251                         problems,
252                         BuilderProblem.Severity.WARNING,
253                         prefix + ".layout",
254                         repository.getId(),
255                         "uses the unsupported value 'legacy', artifact resolution might fail.");
256             }
257         }
258     }
259 
260     // ----------------------------------------------------------------------
261     // Field validation
262     // ----------------------------------------------------------------------
263 
264     /**
265      * Asserts:
266      * <p/>
267      * <ul>
268      * <li><code>string.length == null</code>
269      * <li><code>string.length == 0</code>
270      * </ul>
271      */
272     private static boolean validateStringEmpty(
273             ProblemCollector<BuilderProblem> problems, String fieldName, String string, String message) {
274         if (string == null || string.length() == 0) {
275             return true;
276         }
277 
278         addViolation(problems, BuilderProblem.Severity.WARNING, fieldName, null, message);
279 
280         return false;
281     }
282 
283     /**
284      * Asserts:
285      * <p/>
286      * <ul>
287      * <li><code>string.length != null</code>
288      * <li><code>string.length > 0</code>
289      * </ul>
290      */
291     private static boolean validateStringNotEmpty(
292             ProblemCollector<BuilderProblem> problems, String fieldName, String string, String sourceHint) {
293         if (!validateNotNull(problems, fieldName, string, sourceHint)) {
294             return false;
295         }
296 
297         if (!string.isEmpty()) {
298             return true;
299         }
300 
301         addViolation(problems, BuilderProblem.Severity.ERROR, fieldName, sourceHint, "is missing");
302 
303         return false;
304     }
305 
306     /**
307      * Asserts:
308      * <p/>
309      * <ul>
310      * <li><code>string != null</code>
311      * </ul>
312      */
313     private static boolean validateNotNull(
314             ProblemCollector<BuilderProblem> problems, String fieldName, Object object, String sourceHint) {
315         if (object != null) {
316             return true;
317         }
318 
319         addViolation(problems, BuilderProblem.Severity.ERROR, fieldName, sourceHint, "is missing");
320 
321         return false;
322     }
323 
324     private static boolean validateBannedCharacters(
325             ProblemCollector<BuilderProblem> problems,
326             String fieldName,
327             BuilderProblem.Severity severity,
328             String string,
329             String sourceHint,
330             String banned) {
331         if (string != null) {
332             for (int i = string.length() - 1; i >= 0; i--) {
333                 if (banned.indexOf(string.charAt(i)) >= 0) {
334                     addViolation(
335                             problems,
336                             severity,
337                             fieldName,
338                             sourceHint,
339                             "must not contain any of these characters " + banned + " but found " + string.charAt(i));
340                     return false;
341                 }
342             }
343         }
344 
345         return true;
346     }
347 
348     private static void addViolation(
349             ProblemCollector<BuilderProblem> problems,
350             BuilderProblem.Severity severity,
351             String fieldName,
352             String sourceHint,
353             String message) {
354         StringBuilder buffer = new StringBuilder(256);
355         buffer.append('\'').append(fieldName).append('\'');
356 
357         if (sourceHint != null) {
358             buffer.append(" for ").append(sourceHint);
359         }
360 
361         buffer.append(' ').append(message);
362 
363         problems.reportProblem(new DefaultBuilderProblem(null, -1, -1, null, buffer.toString(), severity));
364     }
365 }