001package org.apache.maven.settings.validation;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.HashSet;
023import java.util.List;
024import java.util.Set;
025
026import org.apache.maven.settings.Mirror;
027import org.apache.maven.settings.Profile;
028import org.apache.maven.settings.Repository;
029import org.apache.maven.settings.Server;
030import org.apache.maven.settings.Settings;
031import org.apache.maven.settings.building.SettingsProblem.Severity;
032import org.apache.maven.settings.building.SettingsProblemCollector;
033import org.codehaus.plexus.component.annotations.Component;
034import org.codehaus.plexus.util.StringUtils;
035
036/**
037 * @author Milos Kleint
038 */
039@Component( role = SettingsValidator.class )
040public class DefaultSettingsValidator
041    implements SettingsValidator
042{
043
044    private static final String ID_REGEX = "[A-Za-z0-9_\\-.]+";
045
046    private static final String ILLEGAL_FS_CHARS = "\\/:\"<>|?*";
047
048    private static final String ILLEGAL_REPO_ID_CHARS = ILLEGAL_FS_CHARS;
049
050    public void validate( Settings settings, SettingsProblemCollector problems )
051    {
052        if ( settings.isUsePluginRegistry() )
053        {
054            addViolation( problems, Severity.WARNING, "usePluginRegistry", null, "is deprecated and has no effect." );
055        }
056
057        List<String> pluginGroups = settings.getPluginGroups();
058
059        if ( pluginGroups != null )
060        {
061            for ( int i = 0; i < pluginGroups.size(); i++ )
062            {
063                String pluginGroup = pluginGroups.get( i ).trim();
064
065                if ( StringUtils.isBlank( pluginGroup ) )
066                {
067                    addViolation( problems, Severity.ERROR, "pluginGroups.pluginGroup[" + i + "]", null,
068                                  "must not be empty" );
069                }
070                else if ( !pluginGroup.matches( ID_REGEX ) )
071                {
072                    addViolation( problems, Severity.ERROR, "pluginGroups.pluginGroup[" + i + "]", null,
073                                  "must denote a valid group id and match the pattern " + ID_REGEX );
074                }
075            }
076        }
077
078        List<Server> servers = settings.getServers();
079
080        if ( servers != null )
081        {
082            Set<String> serverIds = new HashSet<String>();
083
084            for ( int i = 0; i < servers.size(); i++ )
085            {
086                Server server = servers.get( i );
087
088                validateStringNotEmpty( problems, "servers.server[" + i + "].id", server.getId(), null );
089
090                if ( !serverIds.add( server.getId() ) )
091                {
092                    addViolation( problems, Severity.WARNING, "servers.server.id", null,
093                                  "must be unique but found duplicate server with id " + server.getId() );
094                }
095            }
096        }
097
098        List<Mirror> mirrors = settings.getMirrors();
099
100        if ( mirrors != null )
101        {
102            for ( Mirror mirror : mirrors )
103            {
104                validateStringNotEmpty( problems, "mirrors.mirror.id", mirror.getId(), mirror.getUrl() );
105
106                validateBannedCharacters( problems, "mirrors.mirror.id", Severity.WARNING, mirror.getId(), null,
107                                          ILLEGAL_REPO_ID_CHARS );
108
109                if ( "local".equals( mirror.getId() ) )
110                {
111                    addViolation( problems, Severity.WARNING, "mirrors.mirror.id", null, "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        {
126            Set<String> profileIds = new HashSet<String>();
127
128            for ( Profile profile : profiles )
129            {
130                if ( !profileIds.add( profile.getId() ) )
131                {
132                    addViolation( problems, Severity.WARNING, "profiles.profile.id", null,
133                                  "must be unique but found duplicate profile with id " + profile.getId() );
134                }
135
136                String prefix = "profiles.profile[" + profile.getId() + "].";
137
138                validateRepositories( problems, profile.getRepositories(), prefix + "repositories.repository" );
139                validateRepositories( problems, profile.getPluginRepositories(), prefix
140                    + "pluginRepositories.pluginRepository" );
141            }
142        }
143    }
144
145    private void validateRepositories( SettingsProblemCollector problems, List<Repository> repositories, String prefix )
146    {
147        Set<String> repoIds = new HashSet<String>();
148
149        for ( Repository repository : repositories )
150        {
151            validateStringNotEmpty( problems, prefix + ".id", repository.getId(), repository.getUrl() );
152
153            validateBannedCharacters( problems, prefix + ".id", Severity.WARNING, repository.getId(), null,
154                                      ILLEGAL_REPO_ID_CHARS );
155
156            if ( "local".equals( repository.getId() ) )
157            {
158                addViolation( problems, Severity.WARNING, prefix + ".id", null, "must not be 'local'"
159                    + ", this identifier is reserved for the local repository"
160                    + ", using it for other repositories will corrupt your repository metadata." );
161            }
162
163            if ( !repoIds.add( repository.getId() ) )
164            {
165                addViolation( problems, Severity.WARNING, prefix + ".id", null,
166                              "must be unique but found duplicate repository with id " + repository.getId() );
167            }
168
169            validateStringNotEmpty( problems, prefix + ".url", repository.getUrl(), repository.getId() );
170
171            if ( "legacy".equals( repository.getLayout() ) )
172            {
173                addViolation( problems, Severity.WARNING, prefix + ".layout", repository.getId(),
174                              "uses the unsupported value 'legacy', artifact resolution might fail." );
175            }
176        }
177    }
178
179    // ----------------------------------------------------------------------
180    // Field validation
181    // ----------------------------------------------------------------------
182
183    /**
184     * Asserts:
185     * <p/>
186     * <ul>
187     * <li><code>string.length != null</code>
188     * <li><code>string.length > 0</code>
189     * </ul>
190     */
191    private boolean validateStringNotEmpty( SettingsProblemCollector problems, String fieldName, String string,
192                                            String sourceHint )
193    {
194        if ( !validateNotNull( problems, fieldName, string, sourceHint ) )
195        {
196            return false;
197        }
198
199        if ( string.length() > 0 )
200        {
201            return true;
202        }
203
204        addViolation( problems, Severity.ERROR, fieldName, sourceHint, "is missing" );
205
206        return false;
207    }
208
209    /**
210     * Asserts:
211     * <p/>
212     * <ul>
213     * <li><code>string != null</code>
214     * </ul>
215     */
216    private boolean validateNotNull( SettingsProblemCollector problems, String fieldName, Object object,
217                                     String sourceHint )
218    {
219        if ( object != null )
220        {
221            return true;
222        }
223
224        addViolation( problems, Severity.ERROR, fieldName, sourceHint, "is missing" );
225
226        return false;
227    }
228
229    private boolean validateBannedCharacters( SettingsProblemCollector problems, String fieldName, Severity severity,
230                                              String string, String sourceHint, String banned )
231    {
232        if ( string != null )
233        {
234            for ( int i = string.length() - 1; i >= 0; i-- )
235            {
236                if ( banned.indexOf( string.charAt( i ) ) >= 0 )
237                {
238                    addViolation( problems, severity, fieldName, sourceHint,
239                                  "must not contain any of these characters " + banned + " but found "
240                                      + string.charAt( i ) );
241                    return false;
242                }
243            }
244        }
245
246        return true;
247    }
248
249    private void addViolation( SettingsProblemCollector problems, Severity severity, String fieldName,
250                               String sourceHint, String message )
251    {
252        StringBuilder buffer = new StringBuilder( 256 );
253        buffer.append( '\'' ).append( fieldName ).append( '\'' );
254
255        if ( sourceHint != null )
256        {
257            buffer.append( " for " ).append( sourceHint );
258        }
259
260        buffer.append( ' ' ).append( message );
261
262        problems.add( severity, buffer.toString(), -1, -1, null );
263    }
264
265}