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.validation;
20  
21  import javax.inject.Named;
22  import javax.inject.Singleton;
23  
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Set;
27  import java.util.regex.Pattern;
28  
29  import org.apache.maven.settings.Mirror;
30  import org.apache.maven.settings.Profile;
31  import org.apache.maven.settings.Proxy;
32  import org.apache.maven.settings.Repository;
33  import org.apache.maven.settings.Server;
34  import org.apache.maven.settings.Settings;
35  import org.apache.maven.settings.building.SettingsProblem.Severity;
36  import org.apache.maven.settings.building.SettingsProblemCollector;
37  import org.codehaus.plexus.util.StringUtils;
38  
39  /**
40   * @author Milos Kleint
41   */
42  @Named
43  @Singleton
44  public class DefaultSettingsValidator implements SettingsValidator {
45  
46      private static final String ID = "[\\w.-]+";
47      private static final Pattern ID_REGEX = Pattern.compile(ID);
48  
49      private static final String ILLEGAL_REPO_ID_CHARS = "\\/:\"<>|?*"; // ILLEGAL_FS_CHARS
50  
51      @Override
52      public void validate(Settings settings, SettingsProblemCollector problems) {
53          if (settings.isUsePluginRegistry()) {
54              addViolation(problems, Severity.WARNING, "usePluginRegistry", null, "is deprecated and has no effect.");
55          }
56  
57          List<String> pluginGroups = settings.getPluginGroups();
58  
59          if (pluginGroups != null) {
60              for (int i = 0; i < pluginGroups.size(); i++) {
61                  String pluginGroup = pluginGroups.get(i).trim();
62  
63                  if (StringUtils.isBlank(pluginGroup)) {
64                      addViolation(
65                              problems, Severity.ERROR, "pluginGroups.pluginGroup[" + i + "]", null, "must not be empty");
66                  } else if (!ID_REGEX.matcher(pluginGroup).matches()) {
67                      addViolation(
68                              problems,
69                              Severity.ERROR,
70                              "pluginGroups.pluginGroup[" + i + "]",
71                              null,
72                              "must denote a valid group id and match the pattern " + ID);
73                  }
74              }
75          }
76  
77          List<Server> servers = settings.getServers();
78  
79          if (servers != null) {
80              Set<String> serverIds = new HashSet<>();
81  
82              for (int i = 0; i < servers.size(); i++) {
83                  Server server = servers.get(i);
84  
85                  validateStringNotEmpty(problems, "servers.server[" + i + "].id", server.getId(), null);
86  
87                  if (!serverIds.add(server.getId())) {
88                      addViolation(
89                              problems,
90                              Severity.WARNING,
91                              "servers.server.id",
92                              null,
93                              "must be unique but found duplicate server with id " + server.getId());
94                  }
95              }
96          }
97  
98          List<Mirror> mirrors = settings.getMirrors();
99  
100         if (mirrors != null) {
101             for (Mirror mirror : mirrors) {
102                 validateStringNotEmpty(problems, "mirrors.mirror.id", mirror.getId(), mirror.getUrl());
103 
104                 validateBannedCharacters(
105                         problems, "mirrors.mirror.id", Severity.WARNING, mirror.getId(), null, ILLEGAL_REPO_ID_CHARS);
106 
107                 if ("local".equals(mirror.getId())) {
108                     addViolation(
109                             problems,
110                             Severity.WARNING,
111                             "mirrors.mirror.id",
112                             null,
113                             "must not be 'local'"
114                                     + ", this identifier is reserved for the local repository"
115                                     + ", using it for other repositories will corrupt your repository metadata.");
116                 }
117 
118                 validateStringNotEmpty(problems, "mirrors.mirror.url", mirror.getUrl(), mirror.getId());
119 
120                 validateStringNotEmpty(problems, "mirrors.mirror.mirrorOf", mirror.getMirrorOf(), mirror.getId());
121             }
122         }
123 
124         List<Profile> profiles = settings.getProfiles();
125 
126         if (profiles != null) {
127             Set<String> profileIds = new HashSet<>();
128 
129             for (Profile profile : profiles) {
130                 if (!profileIds.add(profile.getId())) {
131                     addViolation(
132                             problems,
133                             Severity.WARNING,
134                             "profiles.profile.id",
135                             null,
136                             "must be unique but found duplicate profile with id " + profile.getId());
137                 }
138 
139                 String prefix = "profiles.profile[" + profile.getId() + "].";
140 
141                 validateRepositories(problems, profile.getRepositories(), prefix + "repositories.repository");
142                 validateRepositories(
143                         problems, profile.getPluginRepositories(), prefix + "pluginRepositories.pluginRepository");
144             }
145         }
146 
147         List<Proxy> proxies = settings.getProxies();
148 
149         if (proxies != null) {
150             Set<String> proxyIds = new HashSet<>();
151 
152             for (Proxy proxy : proxies) {
153                 if (!proxyIds.add(proxy.getId())) {
154                     addViolation(
155                             problems,
156                             Severity.WARNING,
157                             "proxies.proxy.id",
158                             null,
159                             "must be unique but found duplicate proxy with id " + proxy.getId());
160                 }
161                 validateStringNotEmpty(problems, "proxies.proxy.host", proxy.getHost(), proxy.getId());
162             }
163         }
164     }
165 
166     private void validateRepositories(SettingsProblemCollector problems, List<Repository> repositories, String prefix) {
167         Set<String> repoIds = new HashSet<>();
168 
169         for (Repository repository : repositories) {
170             validateStringNotEmpty(problems, prefix + ".id", repository.getId(), repository.getUrl());
171 
172             validateBannedCharacters(
173                     problems, prefix + ".id", Severity.WARNING, repository.getId(), null, ILLEGAL_REPO_ID_CHARS);
174 
175             if ("local".equals(repository.getId())) {
176                 addViolation(
177                         problems,
178                         Severity.WARNING,
179                         prefix + ".id",
180                         null,
181                         "must not be 'local'"
182                                 + ", this identifier is reserved for the local repository"
183                                 + ", using it for other repositories will corrupt your repository metadata.");
184             }
185 
186             if (!repoIds.add(repository.getId())) {
187                 addViolation(
188                         problems,
189                         Severity.WARNING,
190                         prefix + ".id",
191                         null,
192                         "must be unique but found duplicate repository with id " + repository.getId());
193             }
194 
195             validateStringNotEmpty(problems, prefix + ".url", repository.getUrl(), repository.getId());
196 
197             if ("legacy".equals(repository.getLayout())) {
198                 addViolation(
199                         problems,
200                         Severity.WARNING,
201                         prefix + ".layout",
202                         repository.getId(),
203                         "uses the unsupported value 'legacy', artifact resolution might fail.");
204             }
205         }
206     }
207 
208     // ----------------------------------------------------------------------
209     // Field validation
210     // ----------------------------------------------------------------------
211 
212     /**
213      * Asserts:
214      * <p/>
215      * <ul>
216      * <li><code>string.length != null</code>
217      * <li><code>string.length > 0</code>
218      * </ul>
219      */
220     private static boolean validateStringNotEmpty(
221             SettingsProblemCollector problems, String fieldName, String string, String sourceHint) {
222         if (!validateNotNull(problems, fieldName, string, sourceHint)) {
223             return false;
224         }
225 
226         if (string.length() > 0) {
227             return true;
228         }
229 
230         addViolation(problems, Severity.ERROR, fieldName, sourceHint, "is missing");
231 
232         return false;
233     }
234 
235     /**
236      * Asserts:
237      * <p/>
238      * <ul>
239      * <li><code>string != null</code>
240      * </ul>
241      */
242     private static boolean validateNotNull(
243             SettingsProblemCollector problems, String fieldName, Object object, String sourceHint) {
244         if (object != null) {
245             return true;
246         }
247 
248         addViolation(problems, Severity.ERROR, fieldName, sourceHint, "is missing");
249 
250         return false;
251     }
252 
253     private static boolean validateBannedCharacters(
254             SettingsProblemCollector problems,
255             String fieldName,
256             Severity severity,
257             String string,
258             String sourceHint,
259             String banned) {
260         if (string != null) {
261             for (int i = string.length() - 1; i >= 0; i--) {
262                 if (banned.indexOf(string.charAt(i)) >= 0) {
263                     addViolation(
264                             problems,
265                             severity,
266                             fieldName,
267                             sourceHint,
268                             "must not contain any of these characters " + banned + " but found " + string.charAt(i));
269                     return false;
270                 }
271             }
272         }
273 
274         return true;
275     }
276 
277     private static void addViolation(
278             SettingsProblemCollector problems, Severity severity, String fieldName, String sourceHint, String message) {
279         StringBuilder buffer = new StringBuilder(256);
280         buffer.append('\'').append(fieldName).append('\'');
281 
282         if (sourceHint != null) {
283             buffer.append(" for ").append(sourceHint);
284         }
285 
286         buffer.append(' ').append(message);
287 
288         problems.add(severity, buffer.toString(), -1, -1, null);
289     }
290 }