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