001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *  http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.geronimo.genesis;
021    
022    import java.io.File;
023    import java.util.ArrayList;
024    import java.util.HashSet;
025    import java.util.Iterator;
026    import java.util.List;
027    import java.util.Set;
028    import java.util.Collections;
029    
030    import org.apache.maven.artifact.Artifact;
031    import org.apache.maven.artifact.factory.ArtifactFactory;
032    import org.apache.maven.artifact.repository.ArtifactRepository;
033    import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
034    import org.apache.maven.artifact.resolver.ArtifactResolutionException;
035    import org.apache.maven.artifact.resolver.ArtifactResolver;
036    import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
037    import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
038    import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
039    import org.apache.maven.artifact.versioning.VersionRange;
040    import org.apache.maven.model.Dependency;
041    import org.apache.maven.model.Exclusion;
042    import org.apache.maven.plugin.AbstractMojo;
043    import org.apache.maven.plugin.MojoExecutionException;
044    import org.apache.maven.plugin.MojoFailureException;
045    import org.apache.maven.plugin.logging.Log;
046    import org.apache.maven.project.MavenProject;
047    import org.codehaus.plexus.PlexusConstants;
048    import org.codehaus.plexus.PlexusContainer;
049    import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
050    import org.codehaus.plexus.context.Context;
051    import org.codehaus.plexus.context.ContextException;
052    import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
053    
054    import org.apache.geronimo.genesis.dependency.DependencyHelper;
055    import org.apache.geronimo.genesis.util.ArtifactItem;
056    import org.apache.geronimo.genesis.logging.MavenPluginLog;
057    import org.apache.geronimo.genesis.logging.Logging;
058    import org.apache.geronimo.genesis.logging.DelegatingLog;
059    
060    /**
061     * Support for Mojo implementations.
062     *
063     * @version $Rev: 470159 $ $Date: 2006-11-01 17:19:16 -0800 (Wed, 01 Nov 2006) $
064     */
065    public abstract class MojoSupport
066        extends AbstractMojo
067        implements Contextualizable
068    {
069        protected PlexusContainer container;
070    
071        /**
072         * Instance logger.  This is initialized to the value of {@link #getLog}
073         * on execution.
074         */
075        protected Log log;
076        
077        private DependencyHelper dependencyHelper;
078    
079        protected MojoSupport() {
080            // Need to init our logging support before components are initialized and attached
081            Logging.init();
082        }
083    
084        /**
085         * Initializes logging.  Called by {@link #execute}.
086         *
087         * @throws MojoExecutionException   Initialization failed
088         * @throws MojoFailureException     Initialization failed
089         */
090        protected void init() throws MojoExecutionException, MojoFailureException {
091            this.log = getLog();
092    
093            // Install the bridge from JCL to this plugins Log
094            MavenPluginLog.setMojo(this);
095            DelegatingLog.setDelegateType(MavenPluginLog.class);
096            
097            //
098            // NOTE: Using direct lookup because this class may not have been directly configured
099            //
100            try {
101                this.dependencyHelper = (DependencyHelper)container.lookup(DependencyHelper.class.getName());
102            }
103            catch (ComponentLookupException e) {
104                throw new MojoExecutionException("Failed to lookup required components", e);
105            }
106        }
107    
108        public void contextualize(final Context context) throws ContextException {
109            container = (PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY);
110        }
111    
112        /**
113         * Main Mojo execution hook.  Sub-class should use {@link #doExecute} instead.
114         *
115         * @throws MojoExecutionException
116         * @throws MojoFailureException
117         */
118        public void execute() throws MojoExecutionException, MojoFailureException {
119            init();
120    
121            try {
122                doExecute();
123            }
124            catch (Exception e) {
125                //
126                // NOTE: Wrap to avoid truncating the stacktrace
127                //
128                if (e instanceof MojoExecutionException) {
129                    throw new MojoExecutionException(e.getMessage(), e);
130                }
131                else if (e instanceof MojoFailureException) {
132                    MojoFailureException x = new MojoFailureException(e.getMessage());
133                    x.initCause(e);
134                    throw x;
135                }
136                else {
137                    throw new MojoExecutionException(e.getMessage(), e);
138                }
139            }
140            finally {
141                // Reset logging after we are done to avoid complications with other plugins using JCL
142                Logging.reset();
143            }
144        }
145    
146        /**
147         * Sub-class should override to provide custom execution logic.
148         *
149         * @throws Exception    Execution failed
150         */
151        protected void doExecute() throws Exception {
152            // Empty
153        }
154    
155        //
156        // NOTE: These are not abstract because not all sub-classes will need this functionality
157        //
158        
159        /**
160         * Get the Maven project.
161         *
162         * <p>
163         * Sub-class must overridde to provide access.
164         * </p>
165         *
166         * @return  The maven project; never null
167         */
168        protected MavenProject getProject() {
169            throw new Error("Sub-class must override to provide access to : " + MavenProject.class);
170        }
171    
172        /**
173         * Get the artifact repository.
174         *
175         * <p>
176         * Sub-class must overridde to provide access.
177         * </p>
178         *
179         * @return  An artifact repository; never null
180         */
181        protected ArtifactRepository getArtifactRepository() {
182            throw new Error("Sub-class must override to provide access to: " + ArtifactRepository.class);
183        }
184    
185        /**
186         * Get the artifact factory.
187         *
188         * @return  An artifact factory; never null
189         */
190        protected final ArtifactFactory getArtifactFactory() {
191            return dependencyHelper.getArtifactFactory();
192        }
193    
194        /**
195         * Get the artifact resolver.
196         *
197         * @return  An artifact resolver; never null
198         */
199        protected final ArtifactResolver getArtifactResolver() {
200            return dependencyHelper.getArtifactResolver();
201        }
202    
203        /**
204         * Create a new artifact. If no version is specified, it will be retrieved from the dependency
205         * list or from the DependencyManagement section of the pom.
206         *
207         * @param item  The item to create an artifact for
208         * @return      An unresolved artifact for the given item.
209         *
210         * @throws MojoExecutionException   Failed to create artifact
211         */
212        protected Artifact createArtifact(final ArtifactItem item) throws MojoExecutionException {
213            assert item != null;
214    
215            if (item.getVersion() == null) {
216                fillMissingArtifactVersion(item);
217    
218                if (item.getVersion() == null) {
219                    throw new MojoExecutionException("Unable to find artifact version of " + item.getGroupId()
220                        + ":" + item.getArtifactId() + " in either dependency list or in project's dependency management.");
221                }
222            }
223    
224            // Convert the string version to a range
225            VersionRange range;
226            try {
227                range = VersionRange.createFromVersionSpec(item.getVersion());
228                if (log.isDebugEnabled()) {
229                    log.debug("Using version range: " + range);
230                }
231            }
232            catch (InvalidVersionSpecificationException e) {
233                throw new MojoExecutionException("Could not create range for version: " + item.getVersion(), e);
234            }
235            
236            return getArtifactFactory().createDependencyArtifact(
237                item.getGroupId(),
238                item.getArtifactId(),
239                range,
240                item.getType(),
241                item.getClassifier(),
242                Artifact.SCOPE_PROVIDED);
243        }
244    
245        /**
246         * Resolves the Artifact from the remote repository if nessessary. If no version is specified, it will
247         * be retrieved from the dependency list or from the DependencyManagement section of the pom.
248         *
249         *
250         * @param item  The item to create an artifact for; must not be null
251         * @return      The artifact for the given item
252         *
253         * @throws MojoExecutionException   Failed to create artifact
254         */
255        protected Artifact getArtifact(final ArtifactItem item) throws MojoExecutionException {
256            assert item != null;
257            
258            Artifact artifact = createArtifact(item);
259            
260            return resolveArtifact(artifact);
261        }
262    
263        /**
264         * Resolves the Artifact from the remote repository if nessessary. If no version is specified, it will
265         * be retrieved from the dependency list or from the DependencyManagement section of the pom.
266         *
267         * @param artifact      The artifact to be resolved; must not be null
268         * @param transitive    True to resolve the artifact transitivly
269         * @return              The resolved artifact; never null
270         *
271         * @throws MojoExecutionException   Failed to resolve artifact
272         */
273        protected Artifact resolveArtifact(final Artifact artifact, final boolean transitive) throws MojoExecutionException {
274            assert artifact != null;
275    
276            try {
277                if (transitive) {
278                    getArtifactResolver().resolveTransitively(
279                            Collections.singleton(artifact),
280                            getProject().getArtifact(),
281                            getProject().getRemoteArtifactRepositories(),
282                            getArtifactRepository(),
283                            dependencyHelper.getArtifactMetadataSource());
284                }
285                else {
286                    getArtifactResolver().resolve(
287                            artifact,
288                            getProject().getRemoteArtifactRepositories(),
289                            getArtifactRepository());
290                }
291            }
292            catch (ArtifactResolutionException e) {
293                throw new MojoExecutionException("Unable to resolve artifact.", e);
294            }
295            catch (ArtifactNotFoundException e) {
296                throw new MojoExecutionException("Unable to find artifact.", e);
297            }
298    
299            return artifact;
300        }
301    
302        /**
303         * Resolves the Artifact from the remote repository non-transitivly.
304         *
305         * @param artifact  The artifact to be resolved; must not be null
306         * @return          The resolved artifact; never null
307         *
308         * @throws MojoExecutionException   Failed to resolve artifact
309         *
310         * @see #resolveArtifact(Artifact,boolean)
311         */
312        protected Artifact resolveArtifact(final Artifact artifact) throws MojoExecutionException {
313            return resolveArtifact(artifact, false);
314        }
315    
316        /**
317         * Tries to find missing version from dependancy list and dependency management.
318         * If found, the artifact is updated with the correct version.
319         *
320         * @param item  The item to fill in missing version details into
321         */
322        private void fillMissingArtifactVersion(final ArtifactItem item) {
323            log.debug("Attempting to find missing version in " + item.getGroupId() + ":" + item.getArtifactId());
324    
325            List list = getProject().getDependencies();
326    
327            for (int i = 0; i < list.size(); ++i) {
328                Dependency dependency = (Dependency) list.get(i);
329    
330                if (dependency.getGroupId().equals(item.getGroupId())
331                    && dependency.getArtifactId().equals(item.getArtifactId())
332                    && dependency.getType().equals(item.getType()))
333                {
334                    log.debug("Found missing version: " + dependency.getVersion() + " in dependency list.");
335    
336                    item.setVersion(dependency.getVersion());
337    
338                    return;
339                }
340            }
341    
342            list = getProject().getDependencyManagement().getDependencies();
343    
344            for (int i = 0; i < list.size(); i++) {
345                Dependency dependency = (Dependency) list.get(i);
346    
347                if (dependency.getGroupId().equals(item.getGroupId())
348                    && dependency.getArtifactId().equals(item.getArtifactId())
349                    && dependency.getType().equals(item.getType()))
350                {
351                    log.debug("Found missing version: " + dependency.getVersion() + " in dependency management list");
352    
353                    item.setVersion(dependency.getVersion());
354                }
355            }
356        }
357        
358        //
359        // Access to Project artifacts
360        //
361    
362        protected Set getProjectArtifacts(final MavenProject project, final boolean resolve) throws MojoExecutionException {
363            Set artifacts = new HashSet();
364    
365            Iterator dependencies = project.getDependencies().iterator();
366            while (dependencies.hasNext()) {
367                Dependency dep = (Dependency) dependencies.next();
368    
369                String groupId = dep.getGroupId();
370                String artifactId = dep.getArtifactId();
371                VersionRange versionRange = VersionRange.createFromVersion(dep.getVersion());
372                String type = dep.getType();
373                if (type == null) {
374                    type = "jar";
375                }
376    
377                String classifier = dep.getClassifier();
378                boolean optional = dep.isOptional();
379                String scope = dep.getScope();
380                if (scope == null) {
381                    scope = Artifact.SCOPE_COMPILE;
382                }
383    
384                Artifact artifact = getArtifactFactory().createDependencyArtifact(
385                    groupId,
386                    artifactId,
387                    versionRange,
388                    type,
389                    classifier,
390                    scope,
391                    optional);
392    
393                if (scope.equalsIgnoreCase(Artifact.SCOPE_SYSTEM)) {
394                    artifact.setFile(new File(dep.getSystemPath()));
395                }
396    
397                List exclusions = new ArrayList();
398                for (Iterator j = dep.getExclusions().iterator(); j.hasNext();) {
399                    Exclusion e = (Exclusion) j.next();
400                    exclusions.add(e.getGroupId() + ":" + e.getArtifactId());
401                }
402    
403                ArtifactFilter newFilter = new ExcludesArtifactFilter(exclusions);
404                artifact.setDependencyFilter(newFilter);
405                
406                if (resolve && !artifact.isResolved()) {
407                    log.debug("Resolving artifact: " + artifact);
408                    artifact = resolveArtifact(artifact);
409                }
410    
411                artifacts.add(artifact);
412            }
413    
414            return artifacts;
415        }
416    
417        protected Set getProjectArtifacts(final boolean resolve) throws MojoExecutionException {
418            return getProjectArtifacts(getProject(), resolve);
419        }
420    
421        protected Set getProjectArtifacts() throws MojoExecutionException {
422            return getProjectArtifacts(false);
423        }
424    }