001package org.apache.maven.plugins.enforcer.utils;
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.io.File;
023import java.io.IOException;
024import java.io.Reader;
025import java.util.ArrayList;
026import java.util.List;
027
028import org.apache.maven.artifact.Artifact;
029import org.apache.maven.artifact.factory.ArtifactFactory;
030import org.apache.maven.artifact.repository.ArtifactRepository;
031import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
032import org.apache.maven.artifact.resolver.ArtifactResolutionException;
033import org.apache.maven.artifact.resolver.ArtifactResolver;
034import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
035import org.apache.maven.model.Model;
036import org.apache.maven.model.Parent;
037import org.apache.maven.model.Plugin;
038import org.apache.maven.model.ReportPlugin;
039import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
040import org.apache.maven.plugin.logging.Log;
041import org.apache.maven.project.MavenProject;
042import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
043import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
044import org.codehaus.plexus.util.ReaderFactory;
045import org.codehaus.plexus.util.StringUtils;
046import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
047
048/**
049 * The Class EnforcerRuleUtils.
050 *
051 * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
052 */
053public class EnforcerRuleUtils
054{
055
056    /** The factory. */
057    ArtifactFactory factory;
058
059    /** The resolver. */
060    ArtifactResolver resolver;
061
062    /** The local. */
063    ArtifactRepository local;
064
065    /** The remote repositories. */
066    List<ArtifactRepository> remoteRepositories;
067
068    /** The log. */
069    Log log;
070
071    /** The project. */
072    MavenProject project;
073
074    private EnforcerRuleHelper helper;
075
076    /**
077     * Instantiates a new enforcer rule utils.
078     *
079     * @param theFactory the the factory
080     * @param theResolver the the resolver
081     * @param theLocal the the local
082     * @param theRemoteRepositories the the remote repositories
083     * @param project the project
084     * @param theLog the the log
085     */
086    public EnforcerRuleUtils( ArtifactFactory theFactory, ArtifactResolver theResolver, ArtifactRepository theLocal,
087                              List<ArtifactRepository> theRemoteRepositories, MavenProject project, Log theLog )
088    {
089        super();
090        this.factory = theFactory;
091        this.resolver = theResolver;
092        this.local = theLocal;
093        this.remoteRepositories = theRemoteRepositories;
094        this.log = theLog;
095        this.project = project;
096    }
097
098    /**
099     * Instantiates a new enforcer rule utils.
100     *
101     * @param helper the helper
102     */
103    public EnforcerRuleUtils( EnforcerRuleHelper helper )
104    {
105
106        this.helper = helper;
107        // get the various expressions out of the
108        // helper.
109        try
110        {
111            factory = (ArtifactFactory) helper.getComponent( ArtifactFactory.class );
112            resolver = (ArtifactResolver) helper.getComponent( ArtifactResolver.class );
113            local = (ArtifactRepository) helper.evaluate( "${localRepository}" );
114            project = (MavenProject) helper.evaluate( "${project}" );
115            remoteRepositories = project.getRemoteArtifactRepositories();
116        }
117        catch ( ComponentLookupException e )
118        {
119            // TODO Auto-generated catch block
120            e.printStackTrace();
121        }
122        catch ( ExpressionEvaluationException e )
123        {
124            // TODO Auto-generated catch block
125            e.printStackTrace();
126        }
127    }
128
129    /**
130     * Gets the pom model for this file.
131     *
132     * @param pom the pom
133     * @return the model
134     * @throws IOException Signals that an I/O exception has occurred.
135     * @throws XmlPullParserException the xml pull parser exception
136     */
137    private Model readModel( File pom )
138        throws IOException, XmlPullParserException
139    {
140        Reader reader = ReaderFactory.newXmlReader( pom );
141        MavenXpp3Reader xpp3 = new MavenXpp3Reader();
142        Model model = null;
143        try
144        {
145            model = xpp3.read( reader );
146        }
147        finally
148        {
149            reader.close();
150            reader = null;
151        }
152        return model;
153    }
154
155    /**
156     * This method gets the model for the defined artifact. Looks first in the filesystem, then tries to get it from the
157     * repo.
158     *
159     * @param groupId the group id
160     * @param artifactId the artifact id
161     * @param version the version
162     * @param pom the pom
163     * @return the pom model
164     * @throws ArtifactResolutionException the artifact resolution exception
165     * @throws ArtifactNotFoundException the artifact not found exception
166     * @throws XmlPullParserException the xml pull parser exception
167     * @throws IOException Signals that an I/O exception has occurred.
168     */
169    private Model getPomModel( String groupId, String artifactId, String version, File pom )
170        throws ArtifactResolutionException, ArtifactNotFoundException, IOException, XmlPullParserException
171    {
172        Model model = null;
173
174        // do we want to look in the reactor like the
175        // project builder? Would require @aggregator goal
176        // which causes problems in maven core right now
177        // because we also need dependency resolution in
178        // other
179        // rules. (MNG-2277)
180
181        // look in the location specified by pom first.
182        boolean found = false;
183        try
184        {
185            model = readModel( pom );
186
187            // i found a model, lets make sure it's the one
188            // I want
189            found = checkIfModelMatches( groupId, artifactId, version, model );
190        }
191        catch ( IOException e )
192        {
193            // nothing here, but lets look in the repo
194            // before giving up.
195        }
196        catch ( XmlPullParserException e )
197        {
198            // nothing here, but lets look in the repo
199            // before giving up.
200        }
201
202        // i didn't find it in the local file system, go
203        // look in the repo
204        if ( !found )
205        {
206            Artifact pomArtifact = factory.createArtifact( groupId, artifactId, version, null, "pom" );
207            resolver.resolve( pomArtifact, remoteRepositories, local );
208            model = readModel( pomArtifact.getFile() );
209        }
210
211        return model;
212    }
213
214    /**
215     * This method loops through all the parents, getting each pom model and then its parent.
216     *
217     * @param groupId the group id
218     * @param artifactId the artifact id
219     * @param version the version
220     * @param pom the pom
221     * @return the models recursively
222     * @throws ArtifactResolutionException the artifact resolution exception
223     * @throws ArtifactNotFoundException the artifact not found exception
224     * @throws IOException Signals that an I/O exception has occurred.
225     * @throws XmlPullParserException the xml pull parser exception
226     */
227    public List<Model> getModelsRecursively( String groupId, String artifactId, String version, File pom )
228        throws ArtifactResolutionException, ArtifactNotFoundException, IOException, XmlPullParserException
229    {
230        List<Model> models = null;
231        Model model = getPomModel( groupId, artifactId, version, pom );
232
233        Parent parent = model.getParent();
234
235        // recurse into the parent
236        if ( parent != null )
237        {
238            // get the relative path
239            String relativePath = parent.getRelativePath();
240            if ( StringUtils.isEmpty( relativePath ) )
241            {
242                relativePath = "../pom.xml";
243            }
244            // calculate the recursive path
245            File parentPom = new File( pom.getParent(), relativePath );
246
247            // if relative path is a directory, append pom.xml
248            if ( parentPom.isDirectory() )
249            {
250                parentPom = new File( parentPom, "pom.xml" );
251            }
252
253            //@formatter:off
254            models = getModelsRecursively( parent.getGroupId(), parent.getArtifactId(), 
255                                           parent.getVersion(), parentPom );
256            //@formatter:on
257        }
258        else
259        {
260            // only create it here since I'm not at the top
261            models = new ArrayList<Model>();
262        }
263        models.add( model );
264
265        return models;
266    }
267
268    /**
269     * Make sure the model is the one I'm expecting.
270     *
271     * @param groupId the group id
272     * @param artifactId the artifact id
273     * @param version the version
274     * @param model Model being checked.
275     * @return true, if check if model matches
276     */
277    protected boolean checkIfModelMatches( String groupId, String artifactId, String version, Model model )
278    {
279        // try these first.
280        String modelGroup = model.getGroupId();
281        String modelArtifactId = model.getArtifactId();
282        String modelVersion = model.getVersion();
283
284        try
285        {
286            if ( StringUtils.isEmpty( modelGroup ) )
287            {
288                modelGroup = model.getParent().getGroupId();
289            }
290            else
291            {
292                // MENFORCER-30, handle cases where the value is a property like ${project.parent.groupId}
293                modelGroup = (String) helper.evaluate( modelGroup );
294            }
295
296            if ( StringUtils.isEmpty( modelVersion ) )
297            {
298                modelVersion = model.getParent().getVersion();
299            }
300            else
301            {
302                // MENFORCER-30, handle cases where the value is a property like ${project.parent.version}
303                modelVersion = (String) helper.evaluate( modelVersion );
304            }
305
306            // Is this only required for Maven2?
307            modelArtifactId = (String) helper.evaluate( modelArtifactId );
308        }
309        catch ( NullPointerException e )
310        {
311            // this is probably bad. I don't have a valid
312            // group or version and I can't find a
313            // parent????
314            // lets see if it's what we're looking for
315            // anyway.
316        }
317        catch ( ExpressionEvaluationException e )
318        {
319            // as above
320        }
321        // CHECKSTYLE_OFF: LineLength
322        return ( StringUtils.equals( groupId, modelGroup ) && StringUtils.equals( version, modelVersion ) && StringUtils.equals( artifactId,
323                                                                                                                                 modelArtifactId ) );
324        // CHECKSTYLE_ON: LineLength
325    }
326
327    private void resolve( Plugin plugin )
328    {
329        try
330        {
331            plugin.setGroupId( (String) helper.evaluate( plugin.getGroupId() ) );
332            plugin.setArtifactId( (String) helper.evaluate( plugin.getArtifactId() ) );
333            plugin.setVersion( (String) helper.evaluate( plugin.getVersion() ) );
334        }
335        catch ( ExpressionEvaluationException e )
336        {
337            // this should have gone already before
338        }
339    }
340
341    private void resolve( ReportPlugin plugin )
342    {
343        try
344        {
345            plugin.setGroupId( (String) helper.evaluate( plugin.getGroupId() ) );
346            plugin.setArtifactId( (String) helper.evaluate( plugin.getArtifactId() ) );
347            plugin.setVersion( (String) helper.evaluate( plugin.getVersion() ) );
348        }
349        catch ( ExpressionEvaluationException e )
350        {
351            // this should have gone already before
352        }
353    }
354
355    public List<Plugin> resolvePlugins( List<Plugin> plugins )
356    {
357        for ( Plugin plugin : plugins )
358        {
359            resolve( plugin );
360        }
361        return plugins;
362    }
363
364    public List<ReportPlugin> resolveReportPlugins( List<ReportPlugin> reportPlugins )
365    {
366        for ( ReportPlugin plugin : reportPlugins )
367        {
368            resolve( plugin );
369        }
370        return reportPlugins;
371    }
372
373}