001    /**************************************************************************************
002     * Copyright (C) 2009 Progress Software, Inc. All rights reserved.                    *
003     * http://fusesource.com                                                              *
004     * ---------------------------------------------------------------------------------- *
005     * The software in this package is published under the terms of the AGPL license      *
006     * a copy of which has been included with this distribution in the license.txt file.  *
007     **************************************************************************************/
008    package org.fusesource.mvnplugins.graph;
009    
010    import org.apache.maven.project.MavenProject;
011    import org.apache.maven.plugin.MojoExecutionException;
012    import org.apache.maven.plugin.AbstractMojo;
013    import org.apache.maven.artifact.repository.ArtifactRepository;
014    import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
015    import org.apache.maven.artifact.resolver.ArtifactCollector;
016    import org.apache.maven.artifact.resolver.ArtifactResolver;
017    import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
018    import org.apache.maven.artifact.factory.ArtifactFactory;
019    import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
020    import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
021    import org.apache.maven.shared.dependency.tree.DependencyNode;
022    
023    import java.util.ArrayList;
024    import java.io.File;
025    
026    /**
027     * Generates a graph image of the dependencies of the project using the graphviz
028     * tool 'dot'.  You must have the 'dot' executable installed and in your path
029     * before using this goal.
030     * <p/>
031     *
032     * @author chirino
033     * @goal project
034     * @requiresDependencyResolution compile|test|runtime
035     */
036    public class ProjectMojo extends AbstractMojo {
037    
038        /**
039         * @required
040         * @readonly
041         * @parameter expression="${project}"
042         * @since 1.0
043         */
044        private MavenProject project;
045    
046        /**
047         * @required
048         * @readonly
049         * @parameter expression="${localRepository}"
050         * @since 1.0
051         */
052        private ArtifactRepository localRepository;
053    
054        /**
055         * @required
056         * @component
057         * @since 1.0
058         */
059        private ArtifactResolver artifactResolver;
060    
061        /**
062         * @required
063         * @readonly
064         * @component
065         * @since 1.0
066         */
067        private ArtifactFactory artifactFactory;
068    
069        /**
070         * @required
071         * @readonly
072         * @component
073         * @since 1.0
074         */
075        private ArtifactMetadataSource artifactMetadataSource;
076    
077        /**
078         * @required
079         * @readonly
080         * @component
081         */
082        private ArtifactCollector artifactCollector;
083    
084        /**
085         * @required
086         * @readonly
087         * @component
088         * @since 1.0
089         */
090        private DependencyTreeBuilder treeBuilder;
091    
092        /**
093         * The file the diagram will be written to.  Must use a file extension that the dot command supports or just the
094         * '.dot' extension.
095         * <br/>
096         * @parameter default-value="${project.build.directory}/project-graph.png" expression="${graph.target}"
097         */
098        private File target;
099    
100        /**
101         * If set to true, ommitted dependencies will not be drawn.  Dependencies are marked
102         * as ommitted if it would result in a resolution conflict.
103         * <br/>
104         * @parameter default-value="true" expression="${hide-omitted}"
105         */
106        private boolean hideOmitted;
107    
108        /**
109         * If set to true optional dependencies are not drawn.
110         * <br/>
111         * @parameter default-value="false" expression="${hide-optional}"
112         */
113        private boolean hideOptional;
114    
115        /**
116         * If set to true pom dependencies are not drawn.
117         * <br/>
118         * @parameter default-value="true" expression="${hide-poms}"
119         */
120        private boolean hidePoms;
121    
122        /**
123         * A comma seperated list of scopes.  Dependencies which
124         * mach the specified scopes will not be drawn.
125         * <br/>
126         * For example: <code>runtime,test</code>
127         * <br/>
128         * @parameter expression="${hide-scope}"
129         */
130        private String hideScopes;
131    
132        /**
133         * If set to true then depdencies not explicitly defined in the projects
134         * pom will not be drawn.
135         * <br/>
136         * @parameter default-value="false" expression="${hide-transitive}"
137         */
138        private boolean hideTransitive;
139    
140        /**
141         * The label for the graph.
142         * <br/>
143         * @parameter default-value="Depedency Graph for ${project.name}" expression="${graph.label}"
144         */
145        private String label;
146    
147        /**
148         * If true then the 'test scope' and 'optional' attributes are cascaded
149         * down to the dependencies of the original node. 
150         *
151         * <br/>
152         * @parameter default-value="true" expression="${graph.cascade}"
153         */
154        boolean cascade;
155    
156        /**
157         * The direction that the graph will be laid out in.
158         * it can be one of the following values:
159         * <br/>
160         * <code>TB LR BT RL <code>
161         * <br/>
162         * top to bottom, from left to right, from bottom to top, and from right to left, respectively
163         *
164         * <br/>
165         * @parameter default-value="TB" expression="${graph.direction}"
166         */
167        String direction;
168    
169        public void execute() throws MojoExecutionException {
170            try {
171                DependencyVisualizer visualizer = new DependencyVisualizer();
172                visualizer.cascade = cascade;
173                visualizer.direction = direction;
174                visualizer.hideOptional = hideOptional;
175                visualizer.hidePoms = hidePoms;
176                visualizer.hideOmitted = hideOmitted;
177                visualizer.label = label;
178                visualizer.hideTransitive = hideTransitive;
179                visualizer.log = getLog();
180    
181                if (hideScopes != null) {
182                    for (String scope : hideScopes.split(",")) {
183                        visualizer.hideScopes.add(scope);
184                    }
185                }
186    
187                ArrayList<MavenProject> projects = new ArrayList<MavenProject>();
188                collectProjects(projects);
189    
190                for (MavenProject p : projects) {
191                    ArtifactFilter filter = null;
192                    DependencyNode node = treeBuilder.buildDependencyTree(p, localRepository, artifactFactory, artifactMetadataSource, filter, artifactCollector);
193                    visualizer.add(node);
194                }
195    
196                getTarget().getParentFile().mkdirs();
197                visualizer.export(getTarget());
198                getLog().info("Dependency graph exported to: " + getTarget());
199            } catch (DependencyTreeBuilderException e) {
200                throw new MojoExecutionException("Could not build the depedency tree.", e);
201            }
202        }
203    
204        protected void collectProjects(ArrayList<MavenProject> projects) {
205            projects.add(project);
206        }
207    
208        public File getTarget() {
209            return target;
210        }
211    
212    }