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    @Override
051    public void validate( Settings settings, SettingsProblemCollector problems )
052    {
053        if ( settings.isUsePluginRegistry() )
054        {
055            addViolation( problems, Severity.WARNING, "usePluginRegistry", null, "is deprecated and has no effect." );
056        }
057
058        List<String> pluginGroups = settings.getPluginGroups();
059
060        if ( pluginGroups != null )
061        {
062            for ( int i = 0; i < pluginGroups.size(); i++ )
063            {
064                String pluginGroup = pluginGroups.get( i ).trim();
065
066                if ( StringUtils.isBlank( pluginGroup ) )
067                {
068                    addViolation( problems, Severity.ERROR, "pluginGroups.pluginGroup[" + i + "]", null,
069                                  "must not be empty" );
070                }
071                else if ( !pluginGroup.matches( ID_REGEX ) )
072                {
073                    addViolation( problems, Severity.ERROR, "pluginGroups.pluginGroup[" + i + "]", null,
074                                  "must denote a valid group id and match the pattern " + ID_REGEX );
075                }
076            }
077        }
078
079        List<Server> servers = settings.getServers();
080
081        if ( servers != null )
082        {
083            Set<String> serverIds = new HashSet<String>();
084
085            for ( int i = 0; i < servers.size(); i++ )
086            {
087                Server server = servers.get( i );
088
089                validateStringNotEmpty( problems, "servers.server[" + i + "].id", server.getId(), null );
090
091                if ( !serverIds.add( server.getId() ) )
092                {
093                    addViolation( problems, Severity.WARNING, "servers.server.id", null,
094                                  "must be unique but found duplicate server with id " + server.getId() );
095                }
096            }
097        }
098
099        List<Mirror> mirrors = settings.getMirrors();
100
101        if ( mirrors != null )
102        {
103            for ( Mirror mirror : mirrors )
104            {
105                validateStringNotEmpty( problems, "mirrors.mirror.id", mirror.getId(), mirror.getUrl() );
106
107                validateBannedCharacters( problems, "mirrors.mirror.id", Severity.WARNING, mirror.getId(), null,
108                                          ILLEGAL_REPO_ID_CHARS );
109
110                if ( "local".equals( mirror.getId() ) )
111                {
112                    addViolation( problems, Severity.WARNING, "mirrors.mirror.id", null, "must not be 'local'"
113                        + ", this identifier is reserved for the local repository"
114                        + ", using it for other repositories will corrupt your repository metadata." );
115                }
116
117                validateStringNotEmpty( problems, "mirrors.mirror.url", mirror.getUrl(), mirror.getId() );
118
119                validateStringNotEmpty( problems, "mirrors.mirror.mirrorOf", mirror.getMirrorOf(), mirror.getId() );
120            }
121        }
122
123        List<Profile> profiles = settings.getProfiles();
124
125        if ( profiles != null )
126        {
127            Set<String> profileIds = new HashSet<String>();
128
129            for ( Profile profile : profiles )
130            {
131                if ( !profileIds.add( profile.getId() ) )
132                {
133                    addViolation( problems, Severity.WARNING, "profiles.profile.id", 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( problems, profile.getPluginRepositories(), prefix
141                    + "pluginRepositories.pluginRepository" );
142            }
143        }
144    }
145
146    private void validateRepositories( SettingsProblemCollector problems, List<Repository> repositories, String prefix )
147    {
148        Set<String> repoIds = new HashSet<String>();
149
150        for ( Repository repository : repositories )
151        {
152            validateStringNotEmpty( problems, prefix + ".id", repository.getId(), repository.getUrl() );
153
154            validateBannedCharacters( problems, prefix + ".id", Severity.WARNING, repository.getId(), null,
155                                      ILLEGAL_REPO_ID_CHARS );
156
157            if ( "local".equals( repository.getId() ) )
158            {
159                addViolation( problems, Severity.WARNING, prefix + ".id", null, "must not be 'local'"
160                    + ", this identifier is reserved for the local repository"
161                    + ", using it for other repositories will corrupt your repository metadata." );
162            }
163
164            if ( !repoIds.add( repository.getId() ) )
165            {
166                addViolation( problems, Severity.WARNING, prefix + ".id", null,
167                              "must be unique but found duplicate repository with id " + repository.getId() );
168            }
169
170            validateStringNotEmpty( problems, prefix + ".url", repository.getUrl(), repository.getId() );
171
172            if ( "legacy".equals( repository.getLayout() ) )
173            {
174                addViolation( problems, Severity.WARNING, prefix + ".layout", repository.getId(),
175                              "uses the unsupported value 'legacy', artifact resolution might fail." );
176            }
177        }
178    }
179
180    // ----------------------------------------------------------------------
181    // Field validation
182    // ----------------------------------------------------------------------
183
184    /**
185     * Asserts:
186     * <p/>
187     * <ul>
188     * <li><code>string.length != null</code>
189     * <li><code>string.length > 0</code>
190     * </ul>
191     */
192    private boolean validateStringNotEmpty( SettingsProblemCollector problems, String fieldName, String string,
193                                            String sourceHint )
194    {
195        if ( !validateNotNull( problems, fieldName, string, sourceHint ) )
196        {
197            return false;
198        }
199
200        if ( string.length() > 0 )
201        {
202            return true;
203        }
204
205        addViolation( problems, Severity.ERROR, fieldName, sourceHint, "is missing" );
206
207        return false;
208    }
209
210    /**
211     * Asserts:
212     * <p/>
213     * <ul>
214     * <li><code>string != null</code>
215     * </ul>
216     */
217    private boolean validateNotNull( SettingsProblemCollector problems, String fieldName, Object object,
218                                     String sourceHint )
219    {
220        if ( object != null )
221        {
222            return true;
223        }
224
225        addViolation( problems, Severity.ERROR, fieldName, sourceHint, "is missing" );
226
227        return false;
228    }
229
230    private boolean validateBannedCharacters( SettingsProblemCollector problems, String fieldName, Severity severity,
231                                              String string, String sourceHint, String banned )
232    {
233        if ( string != null )
234        {
235            for ( int i = string.length() - 1; i >= 0; i-- )
236            {
237                if ( banned.indexOf( string.charAt( i ) ) >= 0 )
238                {
239                    addViolation( problems, severity, fieldName, sourceHint,
240                                  "must not contain any of these characters " + banned + " but found "
241                                      + string.charAt( i ) );
242                    return false;
243                }
244            }
245        }
246
247        return true;
248    }
249
250    private void addViolation( SettingsProblemCollector problems, Severity severity, String fieldName,
251                               String sourceHint, String message )
252    {
253        StringBuilder buffer = new StringBuilder( 256 );
254        buffer.append( '\'' ).append( fieldName ).append( '\'' );
255
256        if ( sourceHint != null )
257        {
258            buffer.append( " for " ).append( sourceHint );
259        }
260
261        buffer.append( ' ' ).append( message );
262
263        problems.add( severity, buffer.toString(), -1, -1, null );
264    }
265
266}