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