001    package 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    
022    import java.util.HashSet;
023    import java.util.List;
024    import java.util.Set;
025    
026    import org.apache.maven.settings.Mirror;
027    import org.apache.maven.settings.Profile;
028    import org.apache.maven.settings.Repository;
029    import org.apache.maven.settings.Server;
030    import org.apache.maven.settings.Settings;
031    import org.apache.maven.settings.building.SettingsProblem.Severity;
032    import org.apache.maven.settings.building.SettingsProblemCollector;
033    import org.codehaus.plexus.component.annotations.Component;
034    import org.codehaus.plexus.util.StringUtils;
035    
036    /**
037     * @author Milos Kleint
038     */
039    @Component( role = SettingsValidator.class )
040    public 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    }