001package org.apache.maven.plugins.enforcer;
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.artifact.Artifact;
027import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
028import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
029import org.apache.maven.plugin.logging.Log;
030import org.apache.maven.plugins.enforcer.utils.ArtifactUtils;
031import org.apache.maven.project.MavenProject;
032import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
033
034/**
035 * This rule checks that no snapshots are included.
036 *
037 * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
038 */
039public class RequireReleaseDeps
040    extends AbstractBanDependencies
041{
042
043    /**
044     * Allows this rule to execute only when this project is a release.
045     *
046     * @parameter
047     * 
048     * @see {@link #setOnlyWhenRelease(boolean)}
049     * @see {@link #isOnlyWhenRelease()}
050
051     */
052    private boolean onlyWhenRelease = false;
053
054    /**
055     * Allows this rule to fail when the parent is defined as a snapshot.
056     *
057     * @parameter
058     * 
059     * @see {@link #setFailWhenParentIsSnapshot(boolean)}
060     * @see {@link #isFailWhenParentIsSnapshot()}
061     */
062    private boolean failWhenParentIsSnapshot = true;
063
064    /**
065     * Dependencies to ignore when checking for release versions.  For example, inter-module dependencies 
066     * can be excluded from the check and therefore allowed to contain snapshot versions.
067     * 
068     * @see {@link #setExcludes(List)}
069     * @see {@link #getExcludes()}
070     */
071    private List<String> excludes = null;
072
073    /**
074     * Dependencies to include when checking for release versions.  If any of the included dependencies
075     * have snapshot versions, the rule will fail.
076     * 
077     * @see {@link #setIncludes(List)}
078     * @see {@link #getIncludes()}
079     */
080    private List<String> includes = null;
081
082    // Override parent to allow optional ignore of this rule.
083    @Override
084    public void execute( EnforcerRuleHelper helper )
085        throws EnforcerRuleException
086    {
087        boolean callSuper;
088        MavenProject project = null;
089        if ( onlyWhenRelease )
090        {
091            // get the project
092            project = getProject( helper );
093
094            // only call super if this project is a release
095            callSuper = !project.getArtifact().isSnapshot();
096        }
097        else
098        {
099            callSuper = true;
100        }
101        if ( callSuper )
102        {
103            super.execute( helper );
104            if ( failWhenParentIsSnapshot )
105            {
106                if ( project == null )
107                {
108                    project = getProject( helper );
109                }
110                Artifact parentArtifact = project.getParentArtifact();
111                if ( parentArtifact != null && parentArtifact.isSnapshot() )
112                {
113                    throw new EnforcerRuleException( "Parent Cannot be a snapshot: " + parentArtifact.getId() );
114                }
115            }
116        }
117    }
118
119    /**
120     * @param helper
121     * @return The evaluated {@link MavenProject}.
122     * @throws EnforcerRuleException
123     */
124    private MavenProject getProject( EnforcerRuleHelper helper )
125        throws EnforcerRuleException
126    {
127        try
128        {
129            return (MavenProject) helper.evaluate( "${project}" );
130        }
131        catch ( ExpressionEvaluationException eee )
132        {
133            throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", eee );
134        }
135    }
136
137    @Override
138    protected Set<Artifact> checkDependencies( Set<Artifact> dependencies, Log log )
139        throws EnforcerRuleException
140    {
141        Set<Artifact> foundSnapshots = new HashSet<>();
142
143        Set<Artifact> filteredDependencies = filterArtifacts( dependencies );
144        
145        for ( Artifact artifact : filteredDependencies )
146        {
147            if ( artifact.isSnapshot() )
148            {
149                foundSnapshots.add( artifact );
150            }
151        }
152
153        return foundSnapshots;
154    }
155    
156    /*
157     * Filter the dependency artifacts according to the includes and excludes
158     * If includes and excludes are both null, the original set is returned.
159     * 
160     * @param dependencies the list of dependencies to filter
161     * @return the resulting set of dependencies
162     */
163    protected Set<Artifact> filterArtifacts( Set<Artifact> dependencies ) throws EnforcerRuleException 
164    {
165        if ( includes == null && excludes == null )
166        {
167            return dependencies;
168        }
169        
170        Set<Artifact> included;
171        if ( includes != null )
172        {
173            included = ArtifactUtils.checkDependencies( dependencies, includes ); 
174        }
175        else
176        {
177            included = new HashSet<>( dependencies );
178        }
179
180        // anything specifically included should be removed
181        // from the ban list.
182        if ( included != null )
183        {
184            Set<Artifact> excluded = ArtifactUtils.checkDependencies( dependencies, excludes );
185
186            if ( excluded != null )
187            {
188                included.removeAll( excluded );
189            }
190        }
191        return included;
192    }
193
194    public final boolean isOnlyWhenRelease()
195    {
196        return onlyWhenRelease;
197    }
198
199    public final void setOnlyWhenRelease( boolean onlyWhenRelease )
200    {
201        this.onlyWhenRelease = onlyWhenRelease;
202    }
203
204    public final boolean isFailWhenParentIsSnapshot()
205    {
206        return failWhenParentIsSnapshot;
207    }
208
209    public final void setFailWhenParentIsSnapshot( boolean failWhenParentIsSnapshot )
210    {
211        this.failWhenParentIsSnapshot = failWhenParentIsSnapshot;
212    }
213    
214    public final void setExcludes( List<String> excludes )
215    {
216        this.excludes = excludes;
217    }
218    
219    public final List<String> getExcludes()
220    {
221        return excludes;
222    }
223    
224    public void setIncludes( List<String> includes )
225    {
226        this.includes = includes;
227    }
228    
229    public List<String> getIncludes()
230    {
231        return includes;
232    }
233}