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