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