├── .github └── release-drafter.yml ├── .gitignore ├── .mvn ├── extensions.xml └── maven.config ├── CHANGELOG.md ├── Jenkinsfile ├── LICENSE ├── README.markdown ├── pom.xml └── src ├── main ├── java │ └── hudson │ │ └── plugins │ │ └── depgraph_view │ │ ├── AbstractDependencyGraphAction.java │ │ ├── DependencyGraphActionFactory.java │ │ ├── DependencyGraphProjectActionFactory.java │ │ ├── DependencyGraphProperty.java │ │ ├── DependencyGraphViewActionFactory.java │ │ ├── SupportedImageType.java │ │ └── model │ │ ├── display │ │ ├── AbstractDotStringGenerator.java │ │ ├── AbstractGraphStringGenerator.java │ │ ├── DotGeneratorFactory.java │ │ ├── DotStringGenerator.java │ │ ├── GeneratorFactory.java │ │ ├── JsonGeneratorFactory.java │ │ ├── JsonStringGenerator.java │ │ └── LegendDotStringGenerator.java │ │ ├── graph │ │ ├── DependencyGraph.java │ │ ├── DependencyGraphModule.java │ │ ├── EdgeProvider.java │ │ ├── GraphCalculator.java │ │ ├── ProjectNode.java │ │ ├── SubProjectProvider.java │ │ ├── SubprojectCalculator.java │ │ ├── edge │ │ │ ├── BuildTriggerEdge.java │ │ │ ├── BuildTriggerEdgeProvider.java │ │ │ ├── CopyArtifactEdge.java │ │ │ ├── CopyArtifactEdgeProvider.java │ │ │ ├── DependencyEdge.java │ │ │ ├── DependencyGraphEdgeProvider.java │ │ │ ├── Edge.java │ │ │ ├── EdgeProvider.java │ │ │ ├── FanInReverseBuildTriggerEdgeProvider.java │ │ │ ├── MavenDependencyEdge.java │ │ │ ├── ParameterizedTriggerBuilderEdgeProvider.java │ │ │ ├── ParameterizedTriggerEdge.java │ │ │ ├── ParameterizedTriggerEdgeProvider.java │ │ │ ├── PipelineGraphPublisherEdgeProvider.java │ │ │ ├── ReverseBuildTriggerEdge.java │ │ │ └── ReverseBuildTriggerEdgeProvider.java │ │ └── project │ │ │ ├── ParameterizedTriggerSubProjectProvider.java │ │ │ └── SubProjectProvider.java │ │ ├── layout │ │ └── JungSugiyama.java │ │ └── operations │ │ ├── DeleteEdgeOperation.java │ │ ├── EdgeOperation.java │ │ └── PutEdgeOperation.java ├── resources │ ├── hudson │ │ └── plugins │ │ │ └── depgraph_view │ │ │ ├── AbstractDependencyGraphAction │ │ │ ├── index.jelly │ │ │ ├── index_de.properties │ │ │ ├── jsplumb.jelly │ │ │ └── sidepanel.jelly │ │ │ ├── DependencyGraphProperty │ │ │ ├── global.jelly │ │ │ ├── global.properties │ │ │ ├── global_de.properties │ │ │ ├── help-dotExe.html │ │ │ ├── help-dotExe_de.html │ │ │ ├── help-editFunctionInJSViewEnabled.html │ │ │ ├── help-graphRankDirection.html │ │ │ ├── help-projectNameStripRegex.html │ │ │ ├── help-projectNameStripRegexGroup.html │ │ │ ├── help-projectNameStripRegexGroup_de.html │ │ │ ├── help-projectNameStripRegex_de.html │ │ │ └── help-projectNameSuperscriptRegexGroup.html │ │ │ ├── Messages.properties │ │ │ └── Messages_de.properties │ └── index.jelly └── webapp │ ├── css │ ├── depview.css │ └── graph.css │ ├── help-globalConfig.html │ └── js │ ├── jquery-1.7.2.min.js │ ├── jquery-ui-1.8.9.custom.min.js │ ├── jquery.jsPlumb-1.4.1-all-min.js │ └── jsPlumb_depview.js └── test └── java └── hudson └── plugins └── depgraph_view └── model └── graph ├── GraphCalculatorTest.java ├── NormalizeTest.java └── StripProjectNameTest.java /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | tag-template: depgraph-view-$NEXT_PATCH_VERSION 2 | name-template: Dependency Graph Viewer Plugin $NEXT_PATCH_VERSION 🎁 3 | 4 | template: | 5 | $CHANGES 6 | 7 | categories: 8 | - title: 🚨 Removed 9 | label: removed 10 | - title: ⚠️ Deprecated 11 | label: deprecated 12 | - title: 🚀 New features and improvements 13 | labels: 14 | - enhancement 15 | - feature 16 | - title: 🐛 Bug Fixes 17 | labels: 18 | - bug 19 | - fix 20 | - bugfix 21 | - regression 22 | - title: 📝 Documentation updates 23 | label: documentation 24 | - title: 📦 Dependency updates 25 | label: dependencies 26 | - title: 🔧 Internal changes 27 | label: internal 28 | - title: 🚦 Tests 29 | labels: 30 | - test 31 | - tests 32 | 33 | replacers: 34 | - search: '/\[*JENKINS-(\d+)\]*\s*-*\s*/g' 35 | replace: '[JENKINS-$1](https://issues.jenkins-ci.org/browse/JENKINS-$1) - ' 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Eclipse metadata. 4 | # 5 | /.classpath 6 | /.project 7 | /.settings 8 | 9 | # 10 | # IntelliJ metadata. 11 | # 12 | .idea 13 | /*.iml 14 | *.ipr 15 | *.iws 16 | 17 | # 18 | # Maven build folder. 19 | # 20 | /target 21 | 22 | # 23 | # Jenkins test folders. 24 | # 25 | /work 26 | /jobs 27 | -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | io.jenkins.tools.incrementals 4 | git-changelist-maven-extension 5 | 1.7 6 | 7 | 8 | -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -Pconsume-incrementals 2 | -Pmight-produce-incrementals 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### Changelog (0.1 - 0.15) 2 | 3 | All notable changes up to version 0.15 are documented in this file. All future changes will be automatically 4 | logged by release drafter in [GitHub releases](https://github.com/jenkinsci/depgraph-view-plugin/releases). 5 | 6 | 7 | #### Version 0.15.0 (Feb 6, 2020) 8 | 9 | - Switch to semantic versioning 10 | - Add release-drafter release notes 11 | - This will be the last revision dependending on 1.501 12 | 13 | #### Version 0.14 (Feb 5, 2020) 14 | 15 | - [Fix security vulnerability](https://jenkins.io/security/advisory/2019-07-11/) 16 | 17 | #### Version 0.13 (Oct 23, 2017) 18 | 19 | - [Fix security vulnerability](https://jenkins.io/security/advisory/2017-10-23/) 20 | 21 | #### Version 0.12 (Oct 16, 2017) 22 | 23 | - Update version of jsPlumb 24 | 25 | #### Version 0.11 (Mar 22, 2013) 26 | 27 | - Fix invalid ajax calls for `sidepanel.jelly.` 28 | - Show right dependency when the project name for copy artifact is relative 29 | 30 | #### Version 0.10 (Dec 1, 2012) 31 | 32 | - Switch editing of Dependency Graph off by default 33 | - Editing of Dependency Graph is configurable globally 34 | - Disable wrapping of job names 35 | 36 | #### Version 0.9 (Nov 22, 2012) 37 | 38 | - Make jQuery-UI a mandatory dependency 39 | ([JENKINS-15891](https://issues.jenkins-ci.org/browse/JENKINS-15891)) 40 | - Improve Layout algorithm 41 | 42 | #### Version 0.8 (Nov 19, 2012) 43 | 44 | - Use jQuery and jQuery-UI Plugin 45 | - Fix problem with Javascript-Ids from job-names 46 | [JENKINS-15850](https://issues.jenkins-ci.org/browse/JENKINS-15850) 47 | 48 | #### Version 0.7 (Nov 16, 2012) 49 | 50 | - Add ability to switch off graphviz rendering on configuration page 51 | 52 | #### Version 0.6 (Nov 14, 2012) 53 | 54 | - Bugfix for jsPlumb display 55 | 56 | #### Version 0.5 (Nov 13, 2012) 57 | 58 | - ExtensionPoints for Edges and Subprojects of the Graph 59 | - Experimental visualisation via jsPlumb 60 | - Fixed links when jenkins is behind reverse proxy 61 | ([JENKINS-13446](https://issues.jenkins-ci.org/browse/JENKINS-13446), 62 | [JENKINS-12112](https://issues.jenkins-ci.org/browse/JENKINS-12112)) 63 | - Fixed invalid image when graphviz outputs warnings 64 | ([JENKINS-11875](https://issues.jenkins-ci.org/browse/JENKINS-11875)) 65 | 66 | #### Version 0.4 (Sep 27, 2012) 67 | 68 | - Legend now in separate image 69 | 70 | #### Version 0.3 (Sep 25, 2012) 71 | 72 | - Subprojects from the [Parameterized Trigger Plugin](https://plugins.jenkins.io/parameterized-trigger) 73 | are now shown 74 | - Do not rely on default encoding when executing dot 75 | 76 | #### Version 0.2 (Aug 5, 2011) 77 | 78 | - Use getFullDisplayName instead of getName for project names 79 | 80 | #### Version 0.1 (Dec 14, 2010) 81 | 82 | - Initial version 83 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | buildPlugin(useContainerAgent: true, forkCount: '1C', configurations: [ 2 | [platform: 'linux', jdk: 21], 3 | [platform: 'windows', jdk: 17], 4 | ]) 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2007-2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Dependency Graph Viewer 2 | ======================= 3 | 4 | [![Build Status](https://ci.jenkins.io/buildStatus/icon?job=Plugins/depgraph-view-plugin/master)](https://ci.jenkins.io/blue/organizations/jenkins/Plugins%2Fdepgraph-view-plugin/branches/) 5 | [![Jenkins Plugin](https://img.shields.io/jenkins/plugin/v/depgraph-view.svg?label=latest%20version)](https://plugins.jenkins.io/depgraph-view) 6 | [![Jenkins Plugin Installs](https://img.shields.io/jenkins/plugin/i/depgraph-view.svg?color=red)](https://plugins.jenkins.io/depgraph-view) 7 | [![Jenkins Version](https://img.shields.io/badge/Jenkins-2.100-green.svg?label=min.%20Jenkins)](https://jenkins.io/download/) 8 | ![JDK8](https://img.shields.io/badge/jdk-8-yellow.svg?label=min.%20JDK) 9 | [![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 10 | 11 | Shows a dependency graph of the projects using graphviz or jsPlumb. Requires a graphviz installation on the server. 12 | 13 | ### Features 14 | 15 | - Show the dependency graph via graphviz or jsPlumb 16 | - restricted to projects in a view 17 | - restricted to one project 18 | - Show the graphviz source file or jsPlumb json 19 | - Respects access permissions 20 | - Filter project names using regexes 21 | - Show edge by color depending on edge provider 22 | - reverse triggers (fan-in, "build when other project builds"): red 23 | - jenkins dependency graph: black 24 | - parameterized trigger: blue 25 | - maven pipeline dependency graph ("withMaven"): green 26 | - copy artifact (not yet available for pipelines): cyan 27 | 28 | ### Screenshots 29 | 30 | ![](https://user-images.githubusercontent.com/849495/85310461-4dc39e00-b4b4-11ea-86aa-493d096f62e6.png) 31 | 32 | ### Changelog 33 | 34 | - [Changelog for version 0.2 - 0.15](https://github.com/jenkinsci/depgraph-view-plugin/blob/master/CHANGELOG.md) 35 | - [GitHub 1.0.0+ Release Changelog](https://github.com/jenkinsci/depgraph-view-plugin/releases) 36 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 4.0.0 26 | 27 | org.jenkins-ci.plugins 28 | plugin 29 | 4.77 30 | 31 | 32 | 33 | 1.0.6 34 | -SNAPSHOT 35 | 2.414.3 36 | jenkinsci/${project.artifactId}-plugin 37 | 38 | org.jenkins-ci.plugins 39 | depgraph-view 40 | hpi 41 | 42 | Dependency Graph Viewer Plugin 43 | ${revision}${changelist} 44 | 45 | 46 | The MIT license 47 | https://opensource.org/licenses/MIT 48 | repo 49 | 50 | 51 | https://github.com/jenkinsci/${project.artifactId}-plugin 52 | 53 | 54 | wolfs 55 | Stefan Wolf 56 | 57 | 58 | ggrazioli 59 | Guido Grazioli 60 | 61 | 62 | 63 | 64 | 65 | 66 | io.jenkins.tools.bom 67 | bom-2.414.x 68 | 2718.v7e8a_d43b_3f0b_ 69 | import 70 | pom 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.jenkins-ci.plugins 78 | copyartifact 79 | true 80 | 81 | 82 | org.jenkins-ci.plugins 83 | parameterized-trigger 84 | true 85 | 86 | 87 | org.jenkins-ci.plugins 88 | job-fan-in 89 | 1.1.4 90 | true 91 | 92 | 93 | org.jenkins-ci.plugins.workflow 94 | workflow-multibranch 95 | true 96 | 97 | 98 | net.sf.jung 99 | jung-graph-impl 100 | 2.1.1 101 | 102 | 103 | net.sf.jung 104 | jung-algorithms 105 | 2.1.1 106 | 107 | 108 | org.jenkins-ci.plugins.workflow 109 | workflow-step-api 110 | 111 | 112 | org.jenkins-ci.plugins 113 | pipeline-maven 114 | true 115 | 116 | 117 | org.jenkins-ci.plugins 118 | pipeline-build-step 119 | true 120 | 121 | 122 | org.jenkins-ci.plugins 123 | structs 124 | true 125 | 126 | 127 | org.jenkins-ci.plugins 128 | matrix-project 129 | true 130 | 131 | 132 | org.jenkins-ci.plugins 133 | junit 134 | test 135 | 136 | 137 | 138 | 139 | scm:git:https://github.com/${gitHubRepo}.git 140 | scm:git:git@github.com:${gitHubRepo}.git 141 | https://github.com/${gitHubRepo} 142 | ${scmTag} 143 | 144 | 145 | 146 | 147 | repo.jenkins-ci.org 148 | https://repo.jenkins-ci.org/public/ 149 | 150 | 151 | 152 | 153 | 154 | repo.jenkins-ci.org 155 | https://repo.jenkins-ci.org/public/ 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/AbstractDependencyGraphAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view; 24 | 25 | import com.google.common.collect.ListMultimap; 26 | import com.google.inject.Injector; 27 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 28 | import hudson.Launcher; 29 | import hudson.model.AbstractModelObject; 30 | import hudson.model.Job; 31 | import hudson.model.Action; 32 | import hudson.plugins.depgraph_view.DependencyGraphProperty.DescriptorImpl; 33 | import hudson.plugins.depgraph_view.model.display.AbstractGraphStringGenerator; 34 | import hudson.plugins.depgraph_view.model.display.DotGeneratorFactory; 35 | import hudson.plugins.depgraph_view.model.display.GeneratorFactory; 36 | import hudson.plugins.depgraph_view.model.display.JsonGeneratorFactory; 37 | import hudson.plugins.depgraph_view.model.graph.DependencyGraph; 38 | import hudson.plugins.depgraph_view.model.graph.GraphCalculator; 39 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 40 | import hudson.plugins.depgraph_view.model.graph.SubprojectCalculator; 41 | import hudson.plugins.depgraph_view.model.operations.DeleteEdgeOperation; 42 | import hudson.plugins.depgraph_view.model.operations.PutEdgeOperation; 43 | import hudson.util.LogTaskListener; 44 | import jenkins.model.Jenkins; 45 | import jenkins.model.ModelObjectWithContextMenu.ContextMenu; 46 | import org.kohsuke.stapler.StaplerRequest; 47 | import org.kohsuke.stapler.StaplerResponse; 48 | 49 | import javax.servlet.ServletException; 50 | import javax.servlet.http.HttpServletResponse; 51 | import java.io.ByteArrayInputStream; 52 | import java.io.IOException; 53 | import java.io.InputStream; 54 | import java.io.OutputStream; 55 | import java.nio.charset.Charset; 56 | import java.util.Collection; 57 | import java.util.logging.Level; 58 | import java.util.logging.Logger; 59 | import java.util.regex.Matcher; 60 | import java.util.regex.Pattern; 61 | 62 | /** 63 | * Basic action for creating a Dot-Image of the DependencyGraph 64 | */ 65 | @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Everything will be non-null") 66 | public abstract class AbstractDependencyGraphAction implements Action { 67 | 68 | private final Logger LOGGER = Logger.getLogger(Logger.class.getName()); 69 | 70 | private static final Pattern EDGE_PATTERN = Pattern.compile("/(.*)/(.*[^/])(.*)"); 71 | 72 | /** 73 | * This method is called via AJAX to obtain the context menu for this model object, but we don't have one... 74 | * @param request StaplerRequest 75 | * @param response StaplerResponse 76 | * @return the ContextMenu we dont thave any 77 | */ 78 | public ContextMenu doContextMenu(StaplerRequest request, StaplerResponse response) { 79 | return new ContextMenu(); 80 | } 81 | 82 | public void doEdge(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException { 83 | String path = req.getRestOfPath(); 84 | Matcher m = EDGE_PATTERN.matcher(path); 85 | if (m.find( )) { 86 | try { 87 | final String sourceJobName = m.group(1); 88 | final String targetJobName = m.group(2); 89 | if ("PUT".equalsIgnoreCase(req.getMethod())) { 90 | new PutEdgeOperation(sourceJobName, targetJobName).perform(); 91 | } else if ("DELETE".equalsIgnoreCase(req.getMethod())) { 92 | new DeleteEdgeOperation(sourceJobName, targetJobName).perform(); 93 | } 94 | } catch (Exception e) { 95 | rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); 96 | } 97 | } else { 98 | rsp.sendError(HttpServletResponse.SC_NOT_FOUND); 99 | } 100 | } 101 | 102 | /** 103 | * graph.{png,gv,...} is mapped to the corresponding output 104 | * @param req StaplerRequest 105 | * @param rsp StaplerResponse 106 | * @throws IOException if dot fails 107 | * @throws ServletException for other errors 108 | */ 109 | public void doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { 110 | String path = req.getRestOfPath(); 111 | SupportedImageType imageType; 112 | try { 113 | imageType = SupportedImageType.valueOf(path.substring(path.lastIndexOf('.')+1).toUpperCase()); 114 | } catch (Exception e) { 115 | imageType = SupportedImageType.PNG; 116 | } 117 | 118 | GeneratorFactory generatorFactory = (imageType == SupportedImageType.JSON) ? 119 | new JsonGeneratorFactory() : new DotGeneratorFactory(); 120 | AbstractGraphStringGenerator stringGenerator; 121 | if (path.startsWith("/graph.")) { 122 | Injector injector = Jenkins.lookup(Injector.class); 123 | if (injector == null) { 124 | rsp.sendError(HttpServletResponse.SC_NOT_FOUND); 125 | return; 126 | } 127 | GraphCalculator graphCalculator = injector.getInstance(GraphCalculator.class); 128 | DependencyGraph graph = 129 | graphCalculator.generateGraph(GraphCalculator.jobSetToProjectNodeSet(getProjectsForDepgraph())); 130 | ListMultimap projects2Subprojects = 131 | injector.getInstance(SubprojectCalculator.class).generate(graph); 132 | stringGenerator = generatorFactory.newGenerator(graph, projects2Subprojects); 133 | } else if (path.startsWith("/legend.")) { 134 | stringGenerator = generatorFactory.newLegendGenerator(); 135 | } else { 136 | rsp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 137 | return; 138 | } 139 | String graphString = stringGenerator.generate(); 140 | 141 | rsp.setContentType(imageType.contentType); 142 | if (imageType.requiresProcessing) { 143 | runDot(rsp.getOutputStream(), new ByteArrayInputStream(graphString.getBytes(Charset.forName("UTF-8"))), imageType.dotType); 144 | } else { 145 | rsp.getWriter().append(graphString).close(); 146 | } 147 | } 148 | 149 | 150 | /** 151 | * Execute the dot command with given input and output stream 152 | * @param type the parameter for the -T option of the graphviz tools 153 | * @param input the input stream to connect to dot execution 154 | * @param output the output stream to connect to dot execution 155 | * @throws IOException in case of dot execution errors 156 | */ 157 | protected void runDot(OutputStream output, InputStream input, String type) 158 | throws IOException { 159 | DescriptorImpl descriptor = Jenkins.get().getDescriptorByType(DescriptorImpl.class); 160 | String dotPath = descriptor.getDotExeOrDefault(); 161 | Launcher launcher = Jenkins.get().createLauncher(new LogTaskListener(LOGGER, Level.CONFIG)); 162 | try { 163 | launcher.launch() 164 | .cmds(dotPath,"-T" + type, "-Gcharset=UTF-8", "-q1") 165 | .stdin(input) 166 | .stdout(output) 167 | .start().join(); 168 | } catch (InterruptedException e) { 169 | LOGGER.log(Level.SEVERE, "Interrupted while waiting for dot-file to be created",e); 170 | } 171 | finally { 172 | if (output != null) { 173 | output.close(); 174 | } 175 | } 176 | } 177 | 178 | public boolean isGraphvizEnabled() { 179 | return Jenkins.get().getDescriptorByType(DescriptorImpl.class).isGraphvizEnabled(); 180 | } 181 | 182 | public boolean isEditFunctionInJSViewEnabled() { 183 | return Jenkins.get().getDescriptorByType(DescriptorImpl.class).isEditFunctionInJSViewEnabled(); 184 | } 185 | 186 | public String getProjectNameStripRegex() { 187 | return Jenkins.get().getDescriptorByType(DescriptorImpl.class).getProjectNameStripRegex(); 188 | } 189 | 190 | public int getProjectNameStripRegexGroup() { 191 | return Jenkins.get().getDescriptorByType(DescriptorImpl.class).getProjectNameStripRegexGroup(); 192 | } 193 | 194 | /** 195 | * @return projects for which the dependency graph should be calculated 196 | */ 197 | protected abstract Collection> getProjectsForDepgraph(); 198 | 199 | /** 200 | * @return title of the dependency graph page 201 | */ 202 | public abstract String getTitle(); 203 | 204 | /** 205 | * @return object for which the sidepanel.jelly will be shown 206 | */ 207 | public abstract AbstractModelObject getParentObject(); 208 | 209 | @Override 210 | public String getIconFileName() { 211 | return "graph.gif"; 212 | } 213 | 214 | @Override 215 | public String getDisplayName() { 216 | return Messages.AbstractDependencyGraphAction_DependencyGraph(); 217 | } 218 | 219 | @Override 220 | public String getUrlName() { 221 | return "depgraph-view"; 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/DependencyGraphActionFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view; 24 | 25 | import java.util.Collection; 26 | import java.util.Collections; 27 | 28 | import hudson.Extension; 29 | import hudson.model.AbstractModelObject; 30 | import hudson.model.Action; 31 | import hudson.model.Job; 32 | import jenkins.model.TransientActionFactory; 33 | 34 | /** 35 | * Factory to add a dependency graph view action to each project 36 | */ 37 | @Extension 38 | public class DependencyGraphActionFactory extends TransientActionFactory { 39 | /** 40 | * Shows the connected component of the project 41 | */ 42 | public static class DependencyGraphProjectAction extends AbstractDependencyGraphAction { 43 | final private Job project; 44 | 45 | public DependencyGraphProjectAction(Job project) { 46 | this.project = project; 47 | } 48 | 49 | @Override 50 | protected Collection> getProjectsForDepgraph() { 51 | return Collections.>singleton(project); 52 | } 53 | 54 | @Override 55 | public String getTitle() { 56 | return Messages.AbstractDependencyGraphAction_DependencyGraphOf(project.getDisplayName()); 57 | } 58 | 59 | @Override 60 | public AbstractModelObject getParentObject() { 61 | return project; 62 | } 63 | } 64 | 65 | @Override 66 | public Class type() { 67 | return Job.class; 68 | } 69 | 70 | @Override 71 | public Collection createFor(Job target) { 72 | return Collections.singleton(new DependencyGraphProjectAction(target)); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/DependencyGraphProjectActionFactory.java: -------------------------------------------------------------------------------- 1 | package hudson.plugins.depgraph_view; 2 | 3 | import java.util.Collection; 4 | import java.util.Collections; 5 | 6 | import hudson.Extension; 7 | import hudson.model.AbstractProject; 8 | import hudson.model.Action; 9 | import hudson.model.FreeStyleProject; 10 | import hudson.model.TransientProjectActionFactory; 11 | 12 | @Extension 13 | public class DependencyGraphProjectActionFactory extends TransientProjectActionFactory { 14 | 15 | @Override 16 | public Collection createFor(AbstractProject target) { 17 | if (target instanceof FreeStyleProject) { 18 | // the menu link is contributed by the other factory 19 | return Collections.emptyList(); 20 | } else { 21 | return new DependencyGraphActionFactory().createFor(target); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/DependencyGraphProperty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view; 24 | 25 | import hudson.Extension; 26 | import hudson.Functions; 27 | import hudson.Util; 28 | import hudson.model.AbstractDescribableImpl; 29 | import hudson.model.Descriptor; 30 | import hudson.util.FormValidation; 31 | 32 | import java.io.IOException; 33 | import java.util.regex.Pattern; 34 | import java.util.regex.PatternSyntaxException; 35 | 36 | import javax.servlet.ServletException; 37 | 38 | import net.sf.json.JSONObject; 39 | 40 | import org.kohsuke.stapler.DataBoundConstructor; 41 | import org.kohsuke.stapler.QueryParameter; 42 | import org.kohsuke.stapler.StaplerRequest; 43 | 44 | /** 45 | * Class which keeps the configuration for the graphviz 46 | * executable. 47 | */ 48 | public class DependencyGraphProperty extends AbstractDescribableImpl { 49 | 50 | @DataBoundConstructor 51 | public DependencyGraphProperty() { 52 | } 53 | 54 | @Extension 55 | public static class DescriptorImpl extends Descriptor { 56 | 57 | private String dotExe; 58 | 59 | private boolean graphvizEnabled = true; 60 | 61 | private boolean editFunctionInJSViewEnabled = false; 62 | 63 | private String projectNameStripRegex = ".*"; 64 | 65 | private int projectNameStripRegexGroup = 1; 66 | 67 | private int projectNameSuperscriptRegexGroup = -1; 68 | 69 | private String graphRankDirection = "TB"; 70 | 71 | public DescriptorImpl() { 72 | load(); 73 | } 74 | 75 | @Override 76 | public boolean configure( StaplerRequest req, JSONObject o ) { 77 | final JSONObject go = o.optJSONObject("graphviz"); 78 | if(go != null){ 79 | dotExe = Util.fixEmptyAndTrim(go.optString("dotExe")); 80 | graphvizEnabled = true; 81 | } else { 82 | dotExe = null; 83 | graphvizEnabled = false; 84 | } 85 | editFunctionInJSViewEnabled = o.optBoolean("editFunctionInJSViewEnabled"); 86 | setProjectNameStripRegex(o.optString("projectNameStripRegex", ".*")); 87 | setProjectNameStripRegexGroup(o.optInt("projectNameStripRegexGroup", 1)); 88 | setProjectNameSuperscriptRegexGroup(o.optInt("projectNameSuperscriptRegexGroup", 1)); 89 | setGraphRankDirection(o.optString("graphRankDirection", "TB")); 90 | save(); 91 | 92 | return true; 93 | } 94 | 95 | @Override 96 | public String getDisplayName() { 97 | return Messages.DependencyGraphProperty_DependencyGraphViewer(); 98 | } 99 | 100 | public synchronized String getDotExe() { 101 | return dotExe; 102 | } 103 | 104 | public boolean isGraphvizEnabled() { 105 | return graphvizEnabled; 106 | } 107 | 108 | public boolean isEditFunctionInJSViewEnabled() { 109 | return editFunctionInJSViewEnabled; 110 | } 111 | 112 | public synchronized String getProjectNameStripRegex() { 113 | return projectNameStripRegex; 114 | } 115 | 116 | public synchronized int getProjectNameStripRegexGroup() { 117 | return projectNameStripRegexGroup; 118 | } 119 | 120 | public synchronized int getProjectNameSuperscriptRegexGroup() { 121 | return projectNameSuperscriptRegexGroup; 122 | } 123 | 124 | public synchronized String getGraphRankDirection() { 125 | return graphRankDirection; 126 | } 127 | 128 | /** 129 | * @return configured dot executable or a default 130 | */ 131 | public String getDotExeOrDefault() { 132 | if (Util.fixEmptyAndTrim(dotExe) == null) { 133 | return Functions.isWindows() ? "dot.exe" : "dot"; 134 | } else { 135 | return dotExe; 136 | } 137 | } 138 | 139 | public synchronized void setProjectNameStripRegexGroup(int projectNameStripRegexGroup) { 140 | this.projectNameStripRegexGroup = projectNameStripRegexGroup; 141 | save(); 142 | } 143 | 144 | public synchronized void setProjectNameSuperscriptRegexGroup(int projectNameSuperscriptRegexGroup) { 145 | this.projectNameSuperscriptRegexGroup = projectNameSuperscriptRegexGroup; 146 | save(); 147 | } 148 | 149 | public synchronized void setProjectNameStripRegex(String projectNameStripRegex) { 150 | this.projectNameStripRegex = projectNameStripRegex; 151 | save(); 152 | } 153 | 154 | public synchronized void setGraphRankDirection(String graphRankDirection) { 155 | this.graphRankDirection = graphRankDirection; 156 | save(); 157 | } 158 | 159 | public synchronized void setDotExe(String dotPath) { 160 | this.dotExe = dotPath; 161 | save(); 162 | } 163 | 164 | public synchronized void setGraphvizEnabled(boolean graphvizEnabled) { 165 | this.graphvizEnabled = graphvizEnabled; 166 | save(); 167 | } 168 | 169 | public void setEditFunctionInJSViewEnabled(boolean editFunctionInJSViewEnabled) { 170 | this.editFunctionInJSViewEnabled = editFunctionInJSViewEnabled; 171 | save(); 172 | } 173 | 174 | public FormValidation doCheckDotExe(@QueryParameter final String value) { 175 | return FormValidation.validateExecutable(value); 176 | } 177 | 178 | public FormValidation doCheckProjectNameStripRegex(@QueryParameter final String value) 179 | throws IOException, ServletException { 180 | String pattern = Util.fixEmptyAndTrim(value); 181 | if (pattern == null) { 182 | return FormValidation.error(Messages.DependencyGraphProperty_ProjectNameStripRegex_Required()); 183 | } 184 | try { 185 | Pattern.compile(pattern); 186 | } catch (PatternSyntaxException e) { 187 | return FormValidation.error(Messages.DependencyGraphProperty_ProjectNameStripRegex_Invalid()); 188 | } 189 | return FormValidation.ok(); 190 | } 191 | 192 | public FormValidation doCheckProjectNameStripRegexGroup(@QueryParameter final String value) { 193 | return FormValidation.validatePositiveInteger(value); 194 | } 195 | 196 | } 197 | 198 | } 199 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/DependencyGraphViewActionFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view; 24 | 25 | import hudson.Extension; 26 | import hudson.model.AbstractModelObject; 27 | import hudson.model.Action; 28 | import hudson.model.TopLevelItem; 29 | import hudson.model.TransientViewActionFactory; 30 | import hudson.model.View; 31 | import hudson.model.Job; 32 | 33 | import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject; 34 | 35 | import java.util.ArrayList; 36 | import java.util.Collection; 37 | import java.util.Collections; 38 | import java.util.List; 39 | import java.util.stream.Collectors; 40 | 41 | /** 42 | * Factory to a dependency graph view action to all views 43 | */ 44 | @Extension 45 | public class DependencyGraphViewActionFactory extends TransientViewActionFactory { 46 | /** 47 | * Shows the connected components containing the projects of the view 48 | */ 49 | public static class DependencyGraphViewAction extends AbstractDependencyGraphAction 50 | implements Action { 51 | 52 | private View view; 53 | 54 | public DependencyGraphViewAction(View view) { 55 | this.view = view; 56 | } 57 | 58 | @Override 59 | protected Collection> getProjectsForDepgraph() { 60 | Collection items = view.getItems(); 61 | Collection> projects = new ArrayList<>(); 62 | for (TopLevelItem item : items) { 63 | if (item instanceof Job) { 64 | projects.add((Job) item); 65 | } else if (item instanceof WorkflowMultiBranchProject) { 66 | projects.addAll(item.getAllJobs().stream().map(i -> (Job) i).collect(Collectors.toList())); 67 | } 68 | } 69 | return projects; 70 | } 71 | 72 | @Override 73 | public String getTitle() { 74 | return Messages.AbstractDependencyGraphAction_DependencyGraphOf(view.getDisplayName()); 75 | } 76 | 77 | @Override 78 | public AbstractModelObject getParentObject() { 79 | return view; 80 | } 81 | } 82 | 83 | @Override 84 | public List createFor(View v) { 85 | return Collections.singletonList(new DependencyGraphViewAction(v)); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/SupportedImageType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view; 24 | 25 | /** 26 | * Data Structure to encode the content type and the -T argument for the graphviz tools 27 | */ 28 | public enum SupportedImageType { 29 | 30 | PNG("image/png", "png"), 31 | SVG("image/svg", "svg"), 32 | MAP("image/cmapx", "cmapx"), 33 | JSON("text/plain", "json", false), 34 | GV("text/plain", "gv", false), 35 | 36 | ; 37 | 38 | public final String contentType; 39 | public final String dotType; 40 | public final boolean requiresProcessing; 41 | 42 | private SupportedImageType(String contentType, 43 | String dotType, 44 | boolean requiresProcessing) { 45 | this.contentType = contentType; 46 | this.dotType = dotType; 47 | this.requiresProcessing = requiresProcessing; 48 | } 49 | 50 | private SupportedImageType(String contentType, 51 | String dotType) { 52 | this(contentType, dotType, true); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/display/AbstractDotStringGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.display; 24 | 25 | import com.google.common.base.Joiner; 26 | import com.google.common.collect.ListMultimap; 27 | import hudson.plugins.depgraph_view.model.graph.DependencyGraph; 28 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 29 | 30 | /** 31 | * Base class for generating dot representations of the graph. 32 | */ 33 | public abstract class AbstractDotStringGenerator extends AbstractGraphStringGenerator { 34 | 35 | protected String subProjectColor = "#F0F0F0"; 36 | 37 | protected AbstractDotStringGenerator(DependencyGraph graph, ListMultimap projects2Subprojects) { 38 | super(graph, projects2Subprojects); 39 | } 40 | 41 | protected static String escapeString(String toEscape) { 42 | return "\"" + toEscape + "\""; 43 | } 44 | 45 | protected String cluster(String name, String contents, String... options) { 46 | StringBuilder builder = new StringBuilder(); 47 | builder.append("subgraph cluster" + name + " {\n"); 48 | builder.append(contents); 49 | builder.append(Joiner.on("\n").join(options) + "}\n"); 50 | return builder.toString(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/display/AbstractGraphStringGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.display; 24 | 25 | import java.util.ArrayList; 26 | import java.util.Collections; 27 | import java.util.Comparator; 28 | import java.util.List; 29 | 30 | import com.google.common.base.Function; 31 | import com.google.common.collect.ListMultimap; 32 | import com.google.common.collect.Lists; 33 | 34 | import hudson.model.Job; 35 | import hudson.plugins.depgraph_view.model.graph.DependencyGraph; 36 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 37 | import hudson.plugins.depgraph_view.model.graph.edge.Edge; 38 | 39 | /** 40 | * Base class for generating String representations of the graph. 41 | */ 42 | public abstract class AbstractGraphStringGenerator { 43 | // Lexicographic order of the dependencies 44 | protected static final Comparator DEP_COMPARATOR = new Comparator() { 45 | @Override 46 | public int compare(Edge o1, Edge o2) { 47 | int down = (NODE_COMPARATOR.compare(o1.target, o2.target)); 48 | return down != 0 ? down : NODE_COMPARATOR.compare(o1.source, o2.source); 49 | } 50 | }; 51 | 52 | // Compares project nodes by name 53 | protected static final Comparator NODE_COMPARATOR = new Comparator() { 54 | @Override 55 | public int compare(ProjectNode o1, ProjectNode o2) { 56 | return PROJECT_COMPARATOR.compare(o1.getProject(), o2.getProject()); 57 | } 58 | }; 59 | 60 | // Compares projects by name 61 | protected static final Comparator> PROJECT_COMPARATOR = new Comparator>() { 62 | @Override 63 | public int compare(Job o1, Job o2) { 64 | return o1.getFullDisplayName().compareTo(o2.getFullDisplayName()); 65 | } 66 | }; 67 | 68 | protected static final Function PROJECT_NAME_FUNCTION = new Function() { 69 | @Override 70 | public String apply(ProjectNode from) { 71 | return from != null ? from.getName() : ""; 72 | } 73 | }; 74 | 75 | protected ArrayList standaloneProjects; 76 | protected List projectsInDeps; 77 | protected List edges; 78 | protected ListMultimap subJobs; 79 | protected final DependencyGraph graph; 80 | 81 | protected AbstractGraphStringGenerator(DependencyGraph graph, ListMultimap projects2Subprojects) { 82 | this.graph = graph; 83 | this.subJobs = projects2Subprojects; 84 | 85 | /* Sort dependencies (by downstream task first) */ 86 | edges = Lists.newArrayList(graph.getEdges()); 87 | Collections.sort(edges, DEP_COMPARATOR); 88 | 89 | /* Find all projects without dependencies or copied artifacts (stand-alone projects) */ 90 | standaloneProjects = Lists.newArrayList(graph.getIsolatedNodes()); 91 | Collections.sort(standaloneProjects, NODE_COMPARATOR); 92 | 93 | projectsInDeps = Lists.newArrayList(graph.getNodes()); 94 | projectsInDeps.removeAll(standaloneProjects); 95 | Collections.sort(projectsInDeps, NODE_COMPARATOR); 96 | } 97 | 98 | public abstract String generate(); 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/display/DotGeneratorFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.display; 24 | 25 | import com.google.common.collect.ListMultimap; 26 | import hudson.plugins.depgraph_view.model.graph.DependencyGraph; 27 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 28 | 29 | /** 30 | * {@link GeneratorFactory} for the dot format 31 | */ 32 | public class DotGeneratorFactory extends GeneratorFactory { 33 | @Override 34 | public AbstractGraphStringGenerator newGenerator(DependencyGraph graph, ListMultimap subprojects) { 35 | return new DotStringGenerator(graph, subprojects); 36 | } 37 | 38 | @Override 39 | public AbstractGraphStringGenerator newLegendGenerator() { 40 | return new LegendDotStringGenerator(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/display/DotStringGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.display; 24 | 25 | import com.google.common.base.Function; 26 | import com.google.common.base.Joiner; 27 | import com.google.common.collect.ListMultimap; 28 | 29 | import hudson.plugins.depgraph_view.DependencyGraphProperty.DescriptorImpl; 30 | import hudson.plugins.depgraph_view.model.graph.DependencyGraph; 31 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 32 | import hudson.plugins.depgraph_view.model.graph.edge.Edge; 33 | 34 | import java.util.List; 35 | import java.util.regex.Matcher; 36 | import java.util.regex.Pattern; 37 | 38 | import jenkins.model.Jenkins; 39 | import static com.google.common.base.Functions.compose; 40 | import static com.google.common.collect.Lists.transform; 41 | 42 | /** 43 | * Generates a dot string representation of the graph 44 | */ 45 | public class DotStringGenerator extends AbstractDotStringGenerator { 46 | 47 | private String rankDirection; 48 | private static final Function ESCAPE = new Function() { 49 | @Override 50 | public String apply(String from) { 51 | return escapeString(from); 52 | } 53 | }; 54 | 55 | /** 56 | * If the regex pattern matches, the given string will be striped to the given group. 57 | */ 58 | private static final class LabelProjectFunction implements Function{ 59 | 60 | private Pattern pattern; 61 | private int group; 62 | private int supergroup; 63 | 64 | public LabelProjectFunction(Pattern pattern, int group, int supergroup) { 65 | this.pattern = pattern; 66 | this.group = group; 67 | this.supergroup = supergroup; 68 | } 69 | 70 | @Override 71 | public String apply(String name) { 72 | return labelProjectName(name); 73 | } 74 | 75 | // label=<superscript
strippedname> 76 | private String labelProjectName(String name) { 77 | final Matcher matcher = pattern.matcher(name); 78 | if(matcher.matches() && matcher.groupCount() >= group) { 79 | final StringBuilder sb = new StringBuilder(); 80 | if (supergroup > 0 && matcher.groupCount() >= supergroup) { 81 | sb.append("").append(matcher.group(supergroup)).append("
"); 82 | } 83 | sb.append(matcher.group(group)); 84 | return sb.toString(); 85 | } 86 | return name; 87 | } 88 | } 89 | 90 | private final LabelProjectFunction stripFunction; 91 | 92 | 93 | public DotStringGenerator(Jenkins jenkins, DependencyGraph graph, ListMultimap projects2Subprojects) { 94 | super(graph, projects2Subprojects); 95 | int projectNameStripRegexGroup = jenkins.getDescriptorByType(DescriptorImpl.class).getProjectNameStripRegexGroup(); 96 | final String projectNameStripRegex = jenkins.getDescriptorByType(DescriptorImpl.class).getProjectNameStripRegex(); 97 | int projectNameSuperscriptRegexGroup = jenkins.getDescriptorByType(DescriptorImpl.class).getProjectNameSuperscriptRegexGroup(); 98 | this.rankDirection = jenkins.getDescriptorByType(DescriptorImpl.class).getGraphRankDirection(); 99 | 100 | Pattern nameStripPattern = null; 101 | try { 102 | nameStripPattern = Pattern.compile(projectNameStripRegex); 103 | } catch (Exception e) { 104 | nameStripPattern = Pattern.compile(".*"); 105 | } 106 | 107 | stripFunction = new LabelProjectFunction(nameStripPattern, projectNameStripRegexGroup, projectNameSuperscriptRegexGroup); 108 | } 109 | 110 | public DotStringGenerator(DependencyGraph graph, ListMultimap projects2Subprojects) { 111 | this(Jenkins.get(), graph, projects2Subprojects); 112 | } 113 | 114 | /** 115 | * Generates the graphviz code for the given projects and dependencies 116 | * @return graphviz code 117 | */ 118 | @Override 119 | public String generate() { 120 | /**** Build the dot source file ****/ 121 | StringBuilder builder = new StringBuilder(); 122 | 123 | builder.append("digraph {\n"); 124 | builder.append("node [shape=box, style=rounded];\n"); 125 | builder.append("rankdir=").append(rankDirection).append(";\n"); 126 | 127 | /**** First define all the objects and clusters ****/ 128 | 129 | // up/downstream linked jobs 130 | builder.append(cluster("Main", projectsInDependenciesNodes(), "color=invis;")); 131 | 132 | // Stuff not linked to other stuff 133 | List standaloneNames = transform(standaloneProjects, compose(ESCAPE, PROJECT_NAME_FUNCTION)); 134 | builder.append(cluster("Standalone", standaloneProjectNodes(standaloneNames),"color=invis;")); 135 | 136 | /****Now define links between objects ****/ 137 | 138 | // edges 139 | for (Edge edge : edges) { 140 | builder.append(dependencyToEdgeString(edge)); 141 | builder.append(";\n"); 142 | } 143 | 144 | if (!standaloneNames.isEmpty()) { 145 | builder.append("edge[style=\"invisible\",dir=\"none\"];\n" + Joiner.on(" -> ").join(standaloneNames) + ";\n"); 146 | builder.append("edge[style=\"invisible\",dir=\"none\"];\n" + standaloneNames.get(standaloneNames.size() - 1) + " -> \"Dependency Graph\""); 147 | } 148 | 149 | builder.append("}"); 150 | 151 | return builder.toString(); 152 | } 153 | 154 | private String standaloneProjectNodes(List standaloneNames) { 155 | StringBuilder builder = new StringBuilder(); 156 | for (ProjectNode proj : standaloneProjects) { 157 | builder.append(projectToNodeString(proj, subJobs.get(proj))); 158 | builder.append(";\n"); 159 | } 160 | return builder.toString(); 161 | } 162 | 163 | private String projectsInDependenciesNodes() { 164 | StringBuilder stringBuilder = new StringBuilder(); 165 | for (ProjectNode proj : projectsInDeps) { 166 | if (subJobs.containsKey(proj)) { 167 | stringBuilder.append(projectToNodeString(proj, subJobs.get(proj))); 168 | } 169 | else { 170 | stringBuilder.append(projectToNodeString(proj)); 171 | } 172 | stringBuilder.append(";\n"); 173 | } 174 | return stringBuilder.toString(); 175 | } 176 | 177 | private String projectToNodeString(ProjectNode proj) { 178 | return escapeString(proj.getName()) + 179 | " [label=<" + stripFunction.apply(proj.getName()) + "> " + 180 | " tooltip=" + escapeString(proj.getName()) + 181 | " href=" + getEscapedProjectUrl(proj) + "]"; 182 | } 183 | 184 | private String projectToNodeString(ProjectNode proj, List subprojects) { 185 | StringBuilder builder = new StringBuilder(); 186 | builder.append(escapeString(proj.getName())) 187 | .append(" [shape=\"Mrecord\" href=") 188 | .append(getEscapedProjectUrl(proj)) 189 | .append(" label=<\n"); 190 | builder.append(getProjectRow(proj)); 191 | for (ProjectNode subproject : subprojects) { 192 | builder.append(getProjectRow(subproject, "bgcolor=" + escapeString(subProjectColor))).append("\n"); 193 | } 194 | builder.append("
>]"); 195 | return builder.toString(); 196 | } 197 | 198 | private String getProjectRow(ProjectNode project, String... extraColumnProperties) { 199 | return String.format("%s", getEscapedProjectUrl(project), 200 | Joiner.on(" ").join(extraColumnProperties), stripFunction.apply(project.getName())); 201 | } 202 | 203 | private String getEscapedProjectUrl(ProjectNode proj) { 204 | return escapeString(proj.getProject().getAbsoluteUrl()); 205 | } 206 | 207 | private String dependencyToEdgeString(Edge edge, String... options) { 208 | return String.format("%s -> %s [ color=%s %s ] ", escapeString(edge.source.getName()), 209 | escapeString(edge.target.getName()), edge.getColor(), Joiner.on(" ").join(options)); 210 | } 211 | 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/display/GeneratorFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.display; 24 | 25 | import com.google.common.collect.ListMultimap; 26 | import hudson.plugins.depgraph_view.model.graph.DependencyGraph; 27 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 28 | 29 | /** 30 | * Factory Interface for {@link AbstractGraphStringGenerator}s 31 | */ 32 | public abstract class GeneratorFactory { 33 | abstract public AbstractGraphStringGenerator newGenerator(DependencyGraph graph, ListMultimap subprojects); 34 | abstract public AbstractGraphStringGenerator newLegendGenerator(); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/display/JsonGeneratorFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.display; 24 | 25 | import com.google.common.collect.ListMultimap; 26 | import hudson.plugins.depgraph_view.model.graph.DependencyGraph; 27 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 28 | 29 | /** 30 | * {@link GeneratorFactory} for the json format 31 | */ 32 | public class JsonGeneratorFactory extends GeneratorFactory { 33 | @Override 34 | public AbstractGraphStringGenerator newGenerator(DependencyGraph graph, ListMultimap subprojects) { 35 | return new JsonStringGenerator(graph, subprojects); 36 | } 37 | 38 | @Override 39 | public AbstractGraphStringGenerator newLegendGenerator() { 40 | throw new UnsupportedOperationException(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/display/JsonStringGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.display; 24 | 25 | import com.google.common.base.Function; 26 | import com.google.common.collect.ImmutableMap; 27 | import com.google.common.collect.ListMultimap; 28 | import com.google.common.collect.Maps; 29 | import edu.uci.ics.jung.algorithms.cluster.WeakComponentClusterer; 30 | import edu.uci.ics.jung.algorithms.filters.FilterUtils; 31 | import edu.uci.ics.jung.algorithms.layout.Layout; 32 | import edu.uci.ics.jung.graph.Graph; 33 | import hudson.plugins.depgraph_view.model.graph.DependencyGraph; 34 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 35 | import hudson.plugins.depgraph_view.model.graph.edge.Edge; 36 | import hudson.plugins.depgraph_view.model.layout.JungSugiyama; 37 | import net.sf.json.JSONObject; 38 | 39 | import java.awt.*; 40 | import java.awt.geom.Point2D; 41 | import java.util.ArrayList; 42 | import java.util.Collections; 43 | import java.util.HashMap; 44 | import java.util.List; 45 | import java.util.Map; 46 | import java.util.Set; 47 | 48 | import javax.annotation.Nonnull; 49 | import javax.annotation.Nullable; 50 | 51 | import static com.google.common.collect.Lists.newArrayList; 52 | import static com.google.common.collect.Ordering.natural; 53 | 54 | /** 55 | * Generates a Json representation of the graph 56 | */ 57 | public class JsonStringGenerator extends AbstractGraphStringGenerator { 58 | 59 | public JsonStringGenerator(DependencyGraph graph, ListMultimap projects2Subprojects) { 60 | super(graph, projects2Subprojects); 61 | } 62 | 63 | /** 64 | * Generates the json for the given projects and dependencies 65 | * 66 | * @return json model 67 | */ 68 | @Override 69 | public String generate() { 70 | 71 | List> edges = new ArrayList>(); 72 | 73 | for (Edge edge : this.edges) { 74 | addEdge(edges, edge); 75 | } 76 | 77 | // a cluster is a group of jobs having at least one connection to each other through edges 78 | List> clusters = newArrayList(new WeakComponentClusterer().apply(graph.getGraph())); 79 | Collections.sort(clusters, natural().onResultOf(new Function, Integer>() { 80 | @Override 81 | public Integer apply(Set input) { 82 | if (input == null) return 0; 83 | return input.size(); 84 | } 85 | }).reverse()); 86 | 87 | 88 | 89 | List> subgraphs = newArrayList(FilterUtils.createAllInducedSubgraphs(clusters, graph.getGraph())); 90 | List> clusterList = newArrayList(); 91 | for (Graph subgraph : subgraphs) { 92 | if (subgraph.getVertexCount() == 1) { 93 | continue; 94 | } 95 | Layout layout = new JungSugiyama(subgraph); 96 | layout.setSize(new Dimension(300,800)); 97 | layout.initialize(); 98 | 99 | List> nodeList = newArrayList(); 100 | double minX = 300; 101 | double maxX = 0; 102 | double minY = 800; 103 | double maxY = 0; 104 | for (ProjectNode node : subgraph.getVertices()) { 105 | Point2D point = layout.apply(node); 106 | if (point.getX() > maxX) { 107 | maxX = point.getX(); 108 | } 109 | if (point.getX() < minX) { 110 | minX = point.getX(); 111 | } 112 | if (point.getY() > maxY) { 113 | maxY = point.getY(); 114 | } 115 | if (point.getY() < minY) { 116 | minY = point.getY(); 117 | } 118 | } 119 | 120 | for (ProjectNode node : subgraph.getVertices()) { 121 | Point2D point = layout.apply(node); 122 | nodeList.add( 123 | point2Json(point.getX() - minX, point.getY() - minY, node)); 124 | } 125 | Map cluster = 126 | createCluster(nodeList, maxX - minX, maxY - minY); 127 | clusterList.add(cluster); 128 | } 129 | Map standaloneCluster = createStandaloneCluster(); 130 | clusterList.add(standaloneCluster); 131 | 132 | JSONObject json = new JSONObject(); 133 | json.put("edges", edges); 134 | json.put("clusters", clusterList); 135 | 136 | return json.toString(2); 137 | } 138 | 139 | private Map createStandaloneCluster() { 140 | final double nodeXSize = 180; 141 | final double nodeYSize = 90; 142 | final int nodesPerRow = 5; 143 | List> nodeList = newArrayList(); 144 | int row = 0; 145 | int column = 0; 146 | for (ProjectNode node : standaloneProjects) { 147 | nodeList.add(point2Json(column * nodeXSize, row * nodeYSize,node)); 148 | column += 1; 149 | if (column >= nodesPerRow) { 150 | row += 1; 151 | column = 0; 152 | } 153 | } 154 | return createCluster(nodeList, 700.0,(standaloneProjects.size()/nodesPerRow + 1) * nodeYSize); 155 | } 156 | 157 | private Map createCluster(List> nodeList, double hSize, double vSize) { 158 | Map cluster = Maps.newHashMap(); 159 | cluster.put("nodes", nodeList); 160 | cluster.put("hSize", hSize); 161 | cluster.put("vSize", vSize); 162 | return cluster; 163 | } 164 | 165 | private ImmutableMap point2Json(double x, double y, ProjectNode node) { 166 | return ImmutableMap.builder() 167 | .put("name", node.getName()) 168 | .put("fullName", node.getName()) 169 | .put("url", node.getProject().getAbsoluteUrl()) 170 | .put("x", x) 171 | .put("y", y) 172 | .build(); 173 | } 174 | 175 | /** 176 | * Adds an edge between the jobs of the dependency. 177 | * 178 | * @param edges 179 | * the edges to add the dependency to (results in the json) 180 | * @param edge 181 | * the dependency to to be added 182 | */ 183 | private void addEdge(List> edges, Edge edge) { 184 | Map jsonEdge = new HashMap(); 185 | final String fullDisplayNameFrom = edge.source.getName(); 186 | final String fullDisplayNameTo = edge.target.getName(); 187 | jsonEdge.put("from", fullDisplayNameFrom); 188 | jsonEdge.put("to", fullDisplayNameTo); 189 | jsonEdge.put("type", edge.getType()); 190 | edges.add(jsonEdge); 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/display/LegendDotStringGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.display; 24 | 25 | import com.google.common.collect.ArrayListMultimap; 26 | import hudson.plugins.depgraph_view.model.graph.DependencyGraph; 27 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 28 | 29 | /** 30 | * Generates the legend in dot format. 31 | */ 32 | public class LegendDotStringGenerator extends AbstractDotStringGenerator { 33 | 34 | public LegendDotStringGenerator() { 35 | super(new DependencyGraph(), ArrayListMultimap.create()); 36 | } 37 | 38 | @Override 39 | public String generate() { 40 | /**** Build the dot source file ****/ 41 | StringBuilder builder = new StringBuilder(); 42 | 43 | builder.append("digraph {\n"); 44 | builder.append("node [shape=box, style=rounded];\n"); 45 | 46 | builder.append(cluster("Legend", legend())); 47 | 48 | builder.append("}"); 49 | return builder.toString(); 50 | } 51 | 52 | private String legend() { 53 | StringBuilder stringBuilder = new StringBuilder(); 54 | stringBuilder.append("label=\"Legend:\" labelloc=t centered=false color=black node [shape=plaintext]") 55 | .append("\"Build Trigger\"\n") 56 | .append("\"Copy Artifact\"\n") 57 | .append("\"Parameterized Trigger\"\n") 58 | .append("\"Maven Dependency\"\n") 59 | .append("\"Reverse Trigger\"\n") 60 | .append("\"Sub-Project\"\n") 61 | .append("node [style=invis]\n") 62 | .append("a [label=\"\"] b [label=\"\"] c [label=\"\"] d [label=\"\"] e [label=\"\"]") 63 | .append(" f [fillcolor=" + escapeString(subProjectColor) + " style=filled fontcolor=" 64 | + escapeString(subProjectColor) + "]\n") 65 | .append("a -> b [style=invis]\n") 66 | .append("c -> d [style=invis]\n") 67 | .append("{rank=same a -> \"Build Trigger\" [color=black style=bold minlen=2]}\n") 68 | .append("{rank=same b -> \"Copy Artifact\" [color=lightblue minlen=2]}\n") 69 | .append("{rank=same c -> \"Parameterized Trigger\" [color=blue minlen=2]}\n") 70 | .append("{rank=same d -> \"Maven Dependency\" [color=green minlen=2]}\n") 71 | .append("{rank=same e -> \"Reverse Trigger\" [color=red minlen=2]}\n") 72 | .append("{rank=same f -> \"Sub-Project\" [ style=invis]}\n"); 73 | return stringBuilder.toString(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/DependencyGraph.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph; 24 | 25 | import com.google.common.collect.Sets; 26 | import edu.uci.ics.jung.algorithms.filters.VertexPredicateFilter; 27 | import edu.uci.ics.jung.graph.DirectedSparseMultigraph; 28 | import edu.uci.ics.jung.graph.Graph; 29 | import hudson.plugins.depgraph_view.model.graph.edge.Edge; 30 | 31 | import com.google.common.base.Predicate; 32 | 33 | import java.util.Collection; 34 | import java.util.Set; 35 | 36 | /** 37 | * The dependency graph which should be drawn. 38 | */ 39 | public class DependencyGraph { 40 | 41 | private DirectedSparseMultigraph graph = new DirectedSparseMultigraph(); 42 | 43 | public void addEdge(Edge edge) { 44 | graph.addEdge(edge, edge.getNodes()); 45 | } 46 | 47 | public Set addEdgesWithNodes(Iterable edges) { 48 | Set newNodes = Sets.newHashSet(); 49 | for (T edge : edges) { 50 | for (ProjectNode node: edge.getNodes()) { 51 | if (!graph.containsVertex(node)) { 52 | graph.addVertex(node); 53 | newNodes.add(node); 54 | } 55 | } 56 | addEdge(edge); 57 | } 58 | return newNodes; 59 | } 60 | 61 | public void addNodes(Iterable nodes) { 62 | for (T node : nodes) { 63 | graph.addVertex(node); 64 | } 65 | } 66 | 67 | public Collection getEdges() { 68 | return graph.getEdges(); 69 | } 70 | 71 | public Collection findEdgeSet(ProjectNode from, ProjectNode to) { 72 | return graph.findEdgeSet(from, to); 73 | } 74 | 75 | public Collection getNodes() { 76 | return graph.getVertices(); 77 | } 78 | 79 | public Collection getIsolatedNodes() { 80 | return new VertexPredicateFilter(new Predicate() { 81 | @Override 82 | public boolean apply(ProjectNode projectNode) { 83 | return graph.degree(projectNode) == 0; 84 | } 85 | }).apply(graph).getVertices(); 86 | } 87 | 88 | public Graph getGraph() { 89 | return graph; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/DependencyGraphModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph; 24 | 25 | import com.google.inject.AbstractModule; 26 | import com.google.inject.Key; 27 | import com.google.inject.multibindings.Multibinder; 28 | 29 | import hudson.Extension; 30 | import hudson.plugins.depgraph_view.model.graph.edge.BuildTriggerEdgeProvider; 31 | import hudson.plugins.depgraph_view.model.graph.edge.CopyArtifactEdgeProvider; 32 | import hudson.plugins.depgraph_view.model.graph.edge.DependencyGraphEdgeProvider; 33 | import hudson.plugins.depgraph_view.model.graph.edge.EdgeProvider; 34 | import hudson.plugins.depgraph_view.model.graph.edge.FanInReverseBuildTriggerEdgeProvider; 35 | import hudson.plugins.depgraph_view.model.graph.edge.ParameterizedTriggerBuilderEdgeProvider; 36 | import hudson.plugins.depgraph_view.model.graph.edge.ParameterizedTriggerEdgeProvider; 37 | import hudson.plugins.depgraph_view.model.graph.edge.PipelineGraphPublisherEdgeProvider; 38 | import hudson.plugins.depgraph_view.model.graph.edge.ReverseBuildTriggerEdgeProvider; 39 | import hudson.plugins.depgraph_view.model.graph.project.ParameterizedTriggerSubProjectProvider; 40 | import hudson.plugins.depgraph_view.model.graph.project.SubProjectProvider; 41 | 42 | /** 43 | * Guice Module for the DependencyGraph 44 | */ 45 | @Extension 46 | public class DependencyGraphModule extends AbstractModule { 47 | @Override 48 | protected void configure() { 49 | Multibinder edgeProviderMultibinder = Multibinder.newSetBinder(binder(), EdgeProvider.class); 50 | edgeProviderMultibinder.addBinding().to(BuildTriggerEdgeProvider.class); 51 | edgeProviderMultibinder.addBinding().to(ReverseBuildTriggerEdgeProvider.class); 52 | edgeProviderMultibinder.addBinding().to(FanInReverseBuildTriggerEdgeProvider.class); 53 | edgeProviderMultibinder.addBinding().to(ParameterizedTriggerEdgeProvider.class); 54 | edgeProviderMultibinder.addBinding().to(ParameterizedTriggerBuilderEdgeProvider.class); 55 | edgeProviderMultibinder.addBinding().to(CopyArtifactEdgeProvider.class); 56 | edgeProviderMultibinder.addBinding().to(PipelineGraphPublisherEdgeProvider.class); 57 | edgeProviderMultibinder.addBinding().to(DependencyGraphEdgeProvider.class); 58 | Multibinder subProjectProviderMultibinder = Multibinder.newSetBinder(binder(),Key.get(SubProjectProvider.class)); 59 | subProjectProviderMultibinder.addBinding().to(ParameterizedTriggerSubProjectProvider.class); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/EdgeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph; 24 | 25 | import hudson.ExtensionPoint; 26 | import hudson.model.Job; 27 | import hudson.plugins.depgraph_view.model.graph.edge.Edge; 28 | 29 | /** 30 | * This interface is only here to not break other plugin extensions. 31 | * 32 | * This is an extension point which makes it possible to add edges 33 | * to the DependencyGraph which gets drawn. Note that in order to add your own 34 | * EdgeProvider you must not annotate the corresponding subclass with {@link hudson.Extension} 35 | * but instead add a {@link com.google.inject.Module} with a {@link com.google.inject.multibindings.Multibinder} 36 | * which has the {@link hudson.Extension} annotation. For example see {@link hudson.plugins.depgraph_view.model.graph.DependencyGraphModule} 37 | * and {@link hudson.plugins.depgraph_view.model.graph.edge.DependencyGraphEdgeProvider} 38 | */ 39 | @Deprecated 40 | public interface EdgeProvider extends ExtensionPoint { 41 | public Iterable getUpstreamEdgesIncidentWith(Job project); 42 | public Iterable getDownstreamEdgesIncidentWith(Job project); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/GraphCalculator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph; 24 | 25 | import static hudson.plugins.depgraph_view.model.graph.ProjectNode.node; 26 | 27 | import java.util.List; 28 | import java.util.Set; 29 | 30 | import jakarta.inject.Inject; 31 | 32 | import com.google.common.base.Function; 33 | import com.google.common.collect.Iterables; 34 | import com.google.common.collect.Lists; 35 | import com.google.common.collect.Sets; 36 | 37 | import hudson.model.Item; 38 | import hudson.model.Job; 39 | import hudson.plugins.depgraph_view.model.graph.edge.Edge; 40 | import hudson.plugins.depgraph_view.model.graph.edge.EdgeProvider; 41 | 42 | /** 43 | * Generates the {@link DependencyGraph} given a set of {@link EdgeProvider}s. 44 | */ 45 | public class GraphCalculator { 46 | 47 | private Set edgeProviders; 48 | 49 | @Inject 50 | public GraphCalculator(Set edgeProviders) { 51 | this.edgeProviders = Sets.newHashSet(edgeProviders); 52 | } 53 | 54 | public DependencyGraph generateGraph(Iterable initialProjects) { 55 | DependencyGraph graph = new DependencyGraph(); 56 | graph.addNodes(initialProjects); 57 | extendGraph(graph, initialProjects, e -> e::getUpstreamEdgesIncidentWith); 58 | extendGraph(graph, initialProjects, e -> e::getDownstreamEdgesIncidentWith); 59 | return graph; 60 | } 61 | 62 | private void extendGraph(DependencyGraph graph, Iterable fromProjects, 63 | java.util.function.Function, Iterable>> x) { 64 | List newEdges = Lists.newArrayList(); 65 | for (ProjectNode projectNode : fromProjects) { 66 | Job project = projectNode.getProject(); 67 | if (project.hasPermission(Item.READ)) { 68 | for (EdgeProvider edgeProvider : edgeProviders) { 69 | Iterables.addAll(newEdges, x.apply(edgeProvider).apply(project)); 70 | } 71 | } 72 | } 73 | Set newProj = graph.addEdgesWithNodes(newEdges); 74 | if (!newProj.isEmpty()) { 75 | extendGraph(graph, newProj, x); 76 | } 77 | } 78 | 79 | public static Iterable jobSetToProjectNodeSet(Iterable> projects) { 80 | return Iterables.transform(projects, new Function, ProjectNode>() { 81 | @Override 82 | public ProjectNode apply(Job input) { 83 | return input != null ? node(input) : null; 84 | } 85 | }); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/ProjectNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph; 24 | 25 | import org.apache.commons.lang.StringEscapeUtils; 26 | 27 | import com.google.common.base.Preconditions; 28 | import hudson.model.Job; 29 | 30 | /** 31 | * A Node in the DependencyGraph, which corresponds to a Project 32 | */ 33 | public class ProjectNode { 34 | private final Job project; 35 | 36 | public static ProjectNode node(Job project) { 37 | return new ProjectNode(project); 38 | } 39 | 40 | public ProjectNode(Job project) { 41 | Preconditions.checkNotNull(project); 42 | this.project = project; 43 | } 44 | 45 | public String getName() { 46 | return StringEscapeUtils.escapeHtml(project.getFullDisplayName()); 47 | } 48 | 49 | public Job getProject() { 50 | return project; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object o) { 55 | if (this == o) return true; 56 | if (o == null || getClass() != o.getClass()) return false; 57 | 58 | ProjectNode that = (ProjectNode) o; 59 | 60 | if (!project.equals(that.project)) return false; 61 | 62 | return true; 63 | } 64 | 65 | @Override 66 | public int hashCode() { 67 | return project.hashCode(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/SubProjectProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph; 24 | 25 | import hudson.ExtensionPoint; 26 | import hudson.model.Job; 27 | import hudson.plugins.depgraph_view.model.graph.DependencyGraphModule; 28 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 29 | import hudson.plugins.depgraph_view.model.graph.project.ParameterizedTriggerSubProjectProvider; 30 | 31 | /** 32 | * This interface is only here to not break other plugin extensions. 33 | * 34 | * This is an extension point which makes it possible to subprojects 35 | * to the DependencyGraph which gets drawn. Note that in order to add your own 36 | * EdgeProvider you must not annotate the corresponding subclass with {@link hudson.Extension} 37 | * but instead add a {@link com.google.inject.Module} with a {@link com.google.inject.multibindings.Multibinder} 38 | * which has the {@link hudson.Extension} annotation. For example see {@link DependencyGraphModule} 39 | * and {@link ParameterizedTriggerSubProjectProvider} 40 | */ 41 | @Deprecated 42 | public interface SubProjectProvider extends ExtensionPoint { 43 | public Iterable getSubProjectsOf(Job project); 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/SubprojectCalculator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph; 24 | 25 | import com.google.common.collect.ArrayListMultimap; 26 | import com.google.common.collect.ListMultimap; 27 | 28 | import hudson.plugins.depgraph_view.model.graph.project.SubProjectProvider; 29 | 30 | import jakarta.inject.Inject; 31 | import java.util.Collection; 32 | import java.util.Set; 33 | 34 | /** 35 | * Used to calculate the subprojects of a project given a set of providers. 36 | */ 37 | public class SubprojectCalculator { 38 | 39 | private Set providers; 40 | 41 | @Inject 42 | public SubprojectCalculator(Set providers) { 43 | this.providers = providers; 44 | } 45 | 46 | public ListMultimap generate(DependencyGraph graph) { 47 | ListMultimap project2Subprojects = ArrayListMultimap.create(); 48 | Collection nodes = graph.getNodes(); 49 | for (ProjectNode node : nodes) { 50 | for (SubProjectProvider provider : providers) { 51 | project2Subprojects.putAll(node, provider.getSubProjectsOf(node.getProject())); 52 | } 53 | } 54 | return project2Subprojects; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/BuildTriggerEdge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Guido Grazioli 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package hudson.plugins.depgraph_view.model.graph.edge; 23 | 24 | import hudson.model.Job; 25 | 26 | public class BuildTriggerEdge extends DependencyEdge { 27 | 28 | public BuildTriggerEdge(Job upstreamProject, Job downstreamProject) { 29 | super(upstreamProject, downstreamProject); 30 | } 31 | 32 | @Override 33 | public String getColor() { 34 | return "black"; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/BuildTriggerEdgeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.logging.Level; 28 | import java.util.logging.Logger; 29 | 30 | import jakarta.inject.Inject; 31 | 32 | import hudson.model.AbstractProject; 33 | import hudson.model.Action; 34 | import hudson.model.Items; 35 | import hudson.model.Job; 36 | import hudson.model.Run; 37 | import hudson.tasks.BuildTrigger; 38 | import jenkins.model.Jenkins; 39 | 40 | import org.jenkinsci.plugins.workflow.job.WorkflowJob; 41 | import org.jenkinsci.plugins.workflow.support.steps.build.BuildUpstreamNodeAction; 42 | 43 | /** 44 | * {@link EdgeProvider} yielding the dependencies of the Jenkins {@link BuildTrigger} publisher. 45 | */ 46 | public class BuildTriggerEdgeProvider implements EdgeProvider { 47 | 48 | private final Jenkins jenkins; 49 | private final Logger LOGGER = Logger.getLogger(BuildTriggerEdgeProvider.class.getName()); 50 | 51 | @Inject 52 | public BuildTriggerEdgeProvider(Jenkins jenkins) { 53 | this.jenkins = jenkins; 54 | } 55 | 56 | @Override 57 | public Iterable getUpstreamEdgesIncidentWith(Job project) { 58 | List edges = new ArrayList<>(); 59 | for (AbstractProject upstream : jenkins.allItems(AbstractProject.class)) { 60 | BuildTrigger buildTrigger = upstream.getPublishersList().get(BuildTrigger.class); 61 | if (buildTrigger != null 62 | && Items.fromNameList(upstream.getParent(), buildTrigger.getChildProjectsValue(), Job.class) 63 | .contains(project)) { 64 | edges.add(new BuildTriggerEdge(upstream, project)); 65 | } 66 | } 67 | return edges; 68 | } 69 | 70 | @Override 71 | public Iterable getDownstreamEdgesIncidentWith(Job project) { 72 | List edges = new ArrayList<>(); 73 | if (project instanceof AbstractProject) { 74 | BuildTrigger buildTrigger = ((AbstractProject) project).getPublishersList().get(BuildTrigger.class); 75 | if (buildTrigger != null) { 76 | for (Job downstream : Items.fromNameList(project.getParent(), 77 | buildTrigger.getChildProjectsValue(), Job.class)) { 78 | edges.add(new BuildTriggerEdge(project, downstream)); 79 | } 80 | } 81 | } else if (project instanceof WorkflowJob) { 82 | Run lastRun = project.getLastSuccessfulBuild(); 83 | if (lastRun == null) { 84 | lastRun = project.getLastBuild(); 85 | } 86 | if (lastRun != null) { 87 | for (Action action : lastRun.getAllActions()) { 88 | if (action instanceof BuildUpstreamNodeAction) { 89 | BuildUpstreamNodeAction buna = (BuildUpstreamNodeAction) action; 90 | Run upstreamRun = Run.fromExternalizableId(buna.getUpstreamRunId()); 91 | if (upstreamRun != null) { 92 | edges.add(new BuildTriggerEdge(upstreamRun.getParent(), project)); 93 | } 94 | } 95 | } 96 | } else { 97 | LOGGER.log(Level.FINE, "Not graphing edges for project " + project.getFullName() + " because it has not been built yet."); 98 | } 99 | } 100 | return edges; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/CopyArtifactEdge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 26 | 27 | /** 28 | * Represents an {@link Edge} given by the {@link hudson.plugins.copyartifact.CopyArtifact} Builder. 29 | */ 30 | public class CopyArtifactEdge extends Edge { 31 | public CopyArtifactEdge(ProjectNode source, ProjectNode target) { 32 | super(source, target); 33 | } 34 | 35 | @Override 36 | public String getType() { 37 | return "copy"; 38 | } 39 | 40 | @Override 41 | public String getColor() { 42 | return "lightblue"; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/CopyArtifactEdgeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import static hudson.plugins.depgraph_view.model.graph.ProjectNode.node; 26 | 27 | import java.util.Set; 28 | 29 | import jakarta.inject.Inject; 30 | 31 | import com.google.common.collect.Sets; 32 | 33 | import hudson.model.Job; 34 | import hudson.model.Project; 35 | import hudson.plugins.copyartifact.CopyArtifact; 36 | import hudson.tasks.Builder; 37 | import jenkins.model.Jenkins; 38 | 39 | /** 40 | * Provides {@link CopyArtifactEdge}s by inspecting the configuration of the {@link CopyArtifact} Plugin. 41 | */ 42 | public class CopyArtifactEdgeProvider implements EdgeProvider { 43 | 44 | private final Jenkins jenkins; 45 | private final boolean isPluginInstalled; 46 | 47 | @Inject 48 | public CopyArtifactEdgeProvider(Jenkins jenkins) { 49 | this.jenkins = jenkins; 50 | isPluginInstalled = jenkins.getPlugin("copyartifact") != null; 51 | } 52 | 53 | @Override 54 | public Iterable getDownstreamEdgesIncidentWith(Job project) { 55 | Set edges = Sets.newHashSet(); 56 | if (!isPluginInstalled) { 57 | return edges; 58 | } 59 | for (Project downstream : jenkins.allItems(Project.class)) { 60 | for (Builder builder : downstream.getBuilders()) { 61 | if (builder instanceof CopyArtifact) { 62 | Job projectFromName = jenkins.getItem(((CopyArtifact) builder).getProjectName(), 63 | downstream.getParent(), Job.class); 64 | if (projectFromName == project) { 65 | edges.add(new CopyArtifactEdge(node(project), node(downstream))); 66 | } 67 | } 68 | } 69 | } 70 | return edges; 71 | } 72 | 73 | @Override 74 | public Iterable getUpstreamEdgesIncidentWith(Job project) { 75 | Set edges = Sets.newHashSet(); 76 | if (!isPluginInstalled) { 77 | return edges; 78 | } 79 | if (project instanceof Project) { 80 | for (Builder builder : ((Project) project).getBuilders()) { 81 | if (builder instanceof CopyArtifact) { 82 | Job upstream = jenkins.getItem(((CopyArtifact) builder).getProjectName(), 83 | project.getParent(), Job.class); 84 | if (upstream != null) { 85 | edges.add(new CopyArtifactEdge(node(upstream), node(project))); 86 | } 87 | } 88 | } 89 | } 90 | return edges; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/DependencyEdge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import hudson.model.Job; 26 | 27 | import static hudson.plugins.depgraph_view.model.graph.ProjectNode.node; 28 | 29 | /** 30 | * {@link Edge} given by a dependency between projects. 31 | */ 32 | public class DependencyEdge extends Edge { 33 | 34 | public DependencyEdge(Job upstreamProject, Job downstreamProject) { 35 | super(node(upstreamProject), node(downstreamProject)); 36 | } 37 | 38 | @Override 39 | public boolean equals(Object o) { 40 | if (this == o) return true; 41 | if (o == null || ! (o instanceof DependencyEdge)) return false; 42 | 43 | DependencyEdge edge = (DependencyEdge) o; 44 | 45 | if (!source.equals(edge.source)) return false; 46 | if (!target.equals(edge.target)) return false; 47 | if (!getType().equals(edge.getType())) return false; 48 | 49 | return true; 50 | } 51 | 52 | @Override 53 | public String getType() { 54 | return "dep"; 55 | } 56 | 57 | public String getColor() { 58 | return "black"; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/DependencyGraphEdgeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import hudson.model.AbstractProject; 26 | import hudson.model.DependencyGraph; 27 | import hudson.model.Job; 28 | import jenkins.model.Jenkins; 29 | 30 | import jakarta.inject.Inject; 31 | import java.util.ArrayList; 32 | import java.util.List; 33 | 34 | /** 35 | * {@link EdgeProvider} yielding the dependencies of the Jenkins dependency graph. 36 | */ 37 | public class DependencyGraphEdgeProvider implements EdgeProvider { 38 | 39 | private DependencyGraph dependencyGraph; 40 | 41 | @Inject 42 | public DependencyGraphEdgeProvider(Jenkins jenkins) { 43 | dependencyGraph = jenkins.getDependencyGraph(); 44 | } 45 | 46 | @Override 47 | public Iterable getUpstreamEdgesIncidentWith(Job project) { 48 | List dependencies = new ArrayList(); 49 | if (project instanceof AbstractProject) { 50 | dependencies.addAll(dependencyGraph.getUpstreamDependencies((AbstractProject)project)); 51 | } 52 | return getEdges(dependencies); 53 | } 54 | 55 | @Override 56 | public Iterable getDownstreamEdgesIncidentWith(Job project) { 57 | List dependencies = new ArrayList(); 58 | if (project instanceof AbstractProject) { 59 | dependencies.addAll(dependencyGraph.getDownstreamDependencies((AbstractProject)project)); 60 | } 61 | return getEdges(dependencies); 62 | } 63 | 64 | private List getEdges(List dependencies) { 65 | List edges = new ArrayList(); 66 | for (DependencyGraph.Dependency dependency : dependencies) { 67 | edges.add(new BuildTriggerEdge(dependency.getUpstreamProject(), dependency.getDownstreamProject())); 68 | } 69 | return edges; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/Edge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import com.google.common.collect.ImmutableSet; 26 | 27 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 28 | 29 | /** 30 | * Representation of an edge in the DependencyGraph 31 | */ 32 | public abstract class Edge { 33 | public final ProjectNode source; 34 | public final ProjectNode target; 35 | 36 | public Edge(ProjectNode source, ProjectNode target) { 37 | this.source = source; 38 | this.target = target; 39 | } 40 | 41 | public ImmutableSet getNodes() { 42 | return ImmutableSet.of(source, target); 43 | } 44 | 45 | public abstract String getType(); 46 | 47 | public abstract String getColor(); 48 | 49 | @Override 50 | public boolean equals(Object o) { 51 | if (this == o) return true; 52 | if (o == null || getClass() != o.getClass()) return false; 53 | 54 | Edge edge = (Edge) o; 55 | 56 | if (!source.equals(edge.source)) return false; 57 | if (!target.equals(edge.target)) return false; 58 | 59 | return true; 60 | } 61 | 62 | @Override 63 | public int hashCode() { 64 | int result = source.hashCode(); 65 | result = 31 * result + target.hashCode(); 66 | return result; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/EdgeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import hudson.ExtensionPoint; 26 | import hudson.model.Job; 27 | import hudson.plugins.depgraph_view.model.graph.DependencyGraphModule; 28 | 29 | /** 30 | * This is an extension point which makes it possible to add edges 31 | * to the DependencyGraph which gets drawn. Note that in order to add your own 32 | * EdgeProvider you must not annotate the corresponding subclass with {@link hudson.Extension} 33 | * but instead add a {@link com.google.inject.Module} with a {@link com.google.inject.multibindings.Multibinder} 34 | * which has the {@link hudson.Extension} annotation. For example see {@link DependencyGraphModule} 35 | * and {@link DependencyGraphEdgeProvider} 36 | */ 37 | public interface EdgeProvider extends ExtensionPoint { 38 | public Iterable getUpstreamEdgesIncidentWith(Job project); 39 | public Iterable getDownstreamEdgesIncidentWith(Job project); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/FanInReverseBuildTriggerEdgeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | import jakarta.inject.Inject; 29 | 30 | import org.lonkar.jobfanin.FanInReverseBuildTrigger; 31 | 32 | import hudson.model.Items; 33 | import hudson.model.Job; 34 | import hudson.triggers.Trigger; 35 | import jenkins.model.Jenkins; 36 | import jenkins.model.ParameterizedJobMixIn.ParameterizedJob; 37 | 38 | /** 39 | * {@link EdgeProvider} yielding the dependencies of the JobFanIn Plugin {@link FanInReverseBuildTrigger} trigger. 40 | */ 41 | public class FanInReverseBuildTriggerEdgeProvider implements EdgeProvider { 42 | 43 | private final Jenkins jenkins; 44 | private final boolean isPluginInstalled; 45 | 46 | @Inject 47 | public FanInReverseBuildTriggerEdgeProvider(Jenkins jenkins) { 48 | this.jenkins = jenkins; 49 | isPluginInstalled = jenkins.getPlugin("job-fan-in") != null; 50 | } 51 | 52 | @Override 53 | public Iterable getUpstreamEdgesIncidentWith(Job project) { 54 | List edges = new ArrayList<>(); 55 | if (!isPluginInstalled) { 56 | return edges; 57 | } 58 | if (project instanceof ParameterizedJob) { 59 | for (Trigger trigger : ((ParameterizedJob) project).getTriggers().values()) { 60 | if (trigger instanceof FanInReverseBuildTrigger) { 61 | for (Job upstream : Items.fromNameList(project.getParent(), 62 | ((FanInReverseBuildTrigger) trigger).getUpstreamProjects(), Job.class)) { 63 | edges.add(new ReverseBuildTriggerEdge(upstream, project)); 64 | } 65 | } 66 | } 67 | } 68 | return edges; 69 | } 70 | 71 | @Override 72 | public Iterable getDownstreamEdgesIncidentWith(Job project) { 73 | List edges = new ArrayList<>(); 74 | if (!isPluginInstalled) { 75 | return edges; 76 | } 77 | for (ParameterizedJob downstream : jenkins.allItems(ParameterizedJob.class)) { 78 | for (Trigger trigger : downstream.getTriggers().values()) { 79 | if (downstream instanceof Job && trigger instanceof FanInReverseBuildTrigger 80 | && Items.fromNameList(project.getParent(), 81 | ((FanInReverseBuildTrigger) trigger).getUpstreamProjects(), Job.class) 82 | .contains(project)) { 83 | edges.add(new ReverseBuildTriggerEdge(project, (Job) downstream)); 84 | } 85 | } 86 | } 87 | return edges; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/MavenDependencyEdge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Guido Grazioli 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import hudson.model.Job; 26 | 27 | public class MavenDependencyEdge extends DependencyEdge { 28 | 29 | public MavenDependencyEdge(Job upstreamProject, Job downstreamProject) { 30 | super(upstreamProject, downstreamProject); 31 | } 32 | 33 | @Override 34 | public String getType() { 35 | return "maven"; 36 | } 37 | 38 | @Override 39 | public String getColor() { 40 | return "green"; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/ParameterizedTriggerBuilderEdgeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | import jakarta.inject.Inject; 29 | 30 | import hudson.model.Items; 31 | import hudson.model.Job; 32 | import hudson.model.Project; 33 | import hudson.plugins.parameterizedtrigger.BuildTriggerConfig; 34 | import hudson.plugins.parameterizedtrigger.TriggerBuilder; 35 | import hudson.tasks.Builder; 36 | import jenkins.model.Jenkins; 37 | 38 | /** 39 | * {@link EdgeProvider} yielding the dependencies of the Parameterized Trigger Plugin {@link TriggerBuilder} builder. 40 | */ 41 | public class ParameterizedTriggerBuilderEdgeProvider implements EdgeProvider { 42 | 43 | private final Jenkins jenkins; 44 | private final boolean isPluginInstalled; 45 | 46 | @Inject 47 | public ParameterizedTriggerBuilderEdgeProvider(Jenkins jenkins) { 48 | this.jenkins = jenkins; 49 | isPluginInstalled = jenkins.getPlugin("parameterized-trigger") != null; 50 | } 51 | 52 | @Override 53 | public Iterable getUpstreamEdgesIncidentWith(Job project) { 54 | List edges = new ArrayList<>(); 55 | if (!isPluginInstalled) { 56 | return edges; 57 | } 58 | for (Project upstream : jenkins.allItems(Project.class)) { 59 | for (Builder builder : upstream.getBuilders()) { 60 | if (builder instanceof TriggerBuilder) { 61 | for (BuildTriggerConfig config : ((TriggerBuilder) builder).getConfigs()) { 62 | if (Items.fromNameList(upstream.getParent(), config.getProjects(), Job.class) 63 | .contains(project)) { 64 | edges.add(new ParameterizedTriggerEdge(upstream, project)); 65 | } 66 | } 67 | } 68 | } 69 | } 70 | return edges; 71 | } 72 | 73 | @Override 74 | public Iterable getDownstreamEdgesIncidentWith(Job project) { 75 | List edges = new ArrayList<>(); 76 | if (!isPluginInstalled) { 77 | return edges; 78 | } 79 | if (project instanceof Project) { 80 | for (Builder builder : ((Project) project).getBuilders()) { 81 | if (builder instanceof TriggerBuilder) { 82 | for (BuildTriggerConfig config : ((TriggerBuilder) builder).getConfigs()) { 83 | for (Job downstream : Items.fromNameList(project.getParent(), config.getProjects(), 84 | Job.class)) { 85 | edges.add(new ParameterizedTriggerEdge(project, downstream)); 86 | } 87 | } 88 | } 89 | } 90 | } 91 | return edges; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/ParameterizedTriggerEdge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Guido Grazioli 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import hudson.model.Job; 26 | 27 | public class ParameterizedTriggerEdge extends DependencyEdge { 28 | 29 | public ParameterizedTriggerEdge(Job upstreamProject, Job downstreamProject) { 30 | super(upstreamProject, downstreamProject); 31 | } 32 | 33 | @Override 34 | public String getColor() { 35 | return "blue"; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/ParameterizedTriggerEdgeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | import jakarta.inject.Inject; 29 | 30 | import hudson.model.AbstractProject; 31 | import hudson.model.Items; 32 | import hudson.model.Job; 33 | import hudson.plugins.parameterizedtrigger.BuildTrigger; 34 | import hudson.plugins.parameterizedtrigger.BuildTriggerConfig; 35 | import jenkins.model.Jenkins; 36 | 37 | /** 38 | * {@link EdgeProvider} yielding the dependencies of the Parameterized Trigger Plugin {@link BuildTrigger} publisher. 39 | */ 40 | public class ParameterizedTriggerEdgeProvider implements EdgeProvider { 41 | 42 | private final Jenkins jenkins; 43 | private final boolean isPluginInstalled; 44 | 45 | @Inject 46 | public ParameterizedTriggerEdgeProvider(Jenkins jenkins) { 47 | this.jenkins = jenkins; 48 | isPluginInstalled = jenkins.getPlugin("parameterized-trigger") != null; 49 | } 50 | 51 | @Override 52 | public Iterable getUpstreamEdgesIncidentWith(Job project) { 53 | List edges = new ArrayList<>(); 54 | if (!isPluginInstalled) { 55 | return edges; 56 | } 57 | for (AbstractProject upstream : jenkins.allItems(AbstractProject.class)) { 58 | BuildTrigger buildTrigger = upstream.getPublishersList().get(BuildTrigger.class); 59 | if (buildTrigger != null) { 60 | for (BuildTriggerConfig config : buildTrigger.getConfigs()) { 61 | if (Items.fromNameList(upstream.getParent(), config.getProjects(), Job.class).contains(project)) { 62 | edges.add(new ParameterizedTriggerEdge(upstream, project)); 63 | } 64 | } 65 | } 66 | } 67 | return edges; 68 | } 69 | 70 | @Override 71 | public Iterable getDownstreamEdgesIncidentWith(Job project) { 72 | List edges = new ArrayList<>(); 73 | if (!isPluginInstalled) { 74 | return edges; 75 | } 76 | if (project instanceof AbstractProject) { 77 | BuildTrigger buildTrigger = ((AbstractProject) project).getPublishersList().get(BuildTrigger.class); 78 | if (buildTrigger != null) { 79 | for (BuildTriggerConfig config : buildTrigger.getConfigs()) { 80 | for (Job downstream : Items.fromNameList(project.getParent(), config.getProjects(), 81 | Job.class)) { 82 | edges.add(new ParameterizedTriggerEdge(project, downstream)); 83 | } 84 | } 85 | } 86 | } 87 | return edges; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/PipelineGraphPublisherEdgeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Guido Grazioli 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.Map; 28 | 29 | import jakarta.inject.Inject; 30 | 31 | import org.jenkinsci.plugins.pipeline.maven.GlobalPipelineMavenConfig; 32 | import org.jenkinsci.plugins.pipeline.maven.dao.PipelineMavenPluginDao; 33 | import org.jenkinsci.plugins.pipeline.maven.trigger.WorkflowJobDependencyTrigger; 34 | import org.jenkinsci.plugins.workflow.job.WorkflowJob; 35 | 36 | import java.util.logging.Level; 37 | import java.util.logging.Logger; 38 | 39 | import hudson.ExtensionList; 40 | import hudson.model.Job; 41 | import jenkins.model.Jenkins; 42 | 43 | 44 | /** 45 | * {@link EdgeProvider} yielding the dependencies of the Jenkins {@link WorkflowJobDependencyTrigger} trigger. 46 | */ 47 | public class PipelineGraphPublisherEdgeProvider implements EdgeProvider { 48 | 49 | private final Jenkins jenkins; 50 | private final boolean isPluginInstalled; 51 | private final static Logger LOGGER = Logger.getLogger(PipelineGraphPublisherEdgeProvider.class.getName()); 52 | 53 | @Inject 54 | public PipelineGraphPublisherEdgeProvider(Jenkins jenkins) { 55 | this.jenkins = jenkins; 56 | this.isPluginInstalled = jenkins.getPlugin("pipeline-maven") != null; 57 | } 58 | 59 | @Override 60 | public Iterable getUpstreamEdgesIncidentWith(Job project) { 61 | List edges = new ArrayList<>(); 62 | if (!isPluginInstalled) { 63 | return edges; 64 | } 65 | GlobalPipelineMavenConfig globalPMConfig = ExtensionList.lookupSingleton(GlobalPipelineMavenConfig.class); 66 | if (globalPMConfig != null && project instanceof WorkflowJob) { 67 | PipelineMavenPluginDao dao = globalPMConfig.getDao(); 68 | if (project.getLastSuccessfulBuild() != null) { 69 | LOGGER.log(Level.FINEST, "Project" + project.getFullName() + ", build: " + project.getLastSuccessfulBuild().getNumber()); 70 | Map upstreams = dao.listUpstreamJobs(project.getFullName(), project.getLastSuccessfulBuild().getNumber()); 71 | for (String upstreamName : upstreams.keySet()) { 72 | Job upstream = jenkins.getItemByFullName(upstreamName, Job.class); 73 | if (upstream != null) edges.add(new MavenDependencyEdge(upstream, project)); 74 | } 75 | } else { 76 | LOGGER.log(Level.FINE, "Project " + project.getFullName() + ": not graphing edges because it was not built yet"); 77 | } 78 | } 79 | return edges; 80 | } 81 | 82 | @Override 83 | public Iterable getDownstreamEdgesIncidentWith(Job project) { 84 | List edges = new ArrayList<>(); 85 | if (!isPluginInstalled) { 86 | return edges; 87 | } 88 | GlobalPipelineMavenConfig globalPMConfig = ExtensionList.lookupSingleton(GlobalPipelineMavenConfig.class); 89 | if (globalPMConfig != null && project instanceof WorkflowJob) { 90 | PipelineMavenPluginDao dao = globalPMConfig.getDao(); 91 | if (project.getLastSuccessfulBuild() != null) { 92 | LOGGER.log(Level.FINEST, "Project" + project.getFullName() + ", build: " + project.getLastSuccessfulBuild().getNumber()); 93 | List downstreams = dao.listDownstreamJobs(project.getFullName(), project.getLastSuccessfulBuild().getNumber()); 94 | for (String downstreamName : downstreams) { 95 | Job downstream = jenkins.getItemByFullName(downstreamName, Job.class); 96 | if (downstream != null) edges.add(new MavenDependencyEdge(project, downstream)); 97 | } 98 | } else { 99 | LOGGER.log(Level.FINE, "Project " + project.getFullName() + ": not graphing edges because it was not built yet"); 100 | } 101 | } 102 | return edges; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/ReverseBuildTriggerEdge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Guido Grazioli 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import hudson.model.Job; 26 | 27 | public class ReverseBuildTriggerEdge extends DependencyEdge { 28 | 29 | public ReverseBuildTriggerEdge(Job upstreamProject, Job downstreamProject) { 30 | super(upstreamProject, downstreamProject); 31 | } 32 | 33 | @Override 34 | public String getColor() { 35 | return "red"; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/edge/ReverseBuildTriggerEdgeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.edge; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | import jakarta.inject.Inject; 29 | 30 | import hudson.model.Items; 31 | import hudson.model.Job; 32 | import hudson.triggers.Trigger; 33 | import jenkins.model.Jenkins; 34 | import jenkins.model.ParameterizedJobMixIn.ParameterizedJob; 35 | import jenkins.triggers.ReverseBuildTrigger; 36 | 37 | /** 38 | * {@link EdgeProvider} yielding the dependencies of the Jenkins {@link ReverseBuildTrigger} trigger. 39 | */ 40 | public class ReverseBuildTriggerEdgeProvider implements EdgeProvider { 41 | 42 | private final Jenkins jenkins; 43 | 44 | @Inject 45 | public ReverseBuildTriggerEdgeProvider(Jenkins jenkins) { 46 | this.jenkins = jenkins; 47 | } 48 | 49 | @Override 50 | public Iterable getUpstreamEdgesIncidentWith(Job project) { 51 | List edges = new ArrayList<>(); 52 | if (project instanceof ParameterizedJob) { 53 | for (Trigger trigger : ((ParameterizedJob) project).getTriggers().values()) { 54 | if (trigger instanceof ReverseBuildTrigger) { 55 | for (Job upstream : Items.fromNameList(project.getParent(), 56 | ((ReverseBuildTrigger) trigger).getUpstreamProjects(), Job.class)) { 57 | edges.add(new ReverseBuildTriggerEdge(upstream, project)); 58 | } 59 | } 60 | } 61 | } 62 | return edges; 63 | } 64 | 65 | @Override 66 | public Iterable getDownstreamEdgesIncidentWith(Job project) { 67 | List edges = new ArrayList<>(); 68 | for (ParameterizedJob downstream : jenkins.allItems(ParameterizedJob.class)) { 69 | for (Trigger trigger : downstream.getTriggers().values()) { 70 | if (downstream instanceof Job 71 | && trigger instanceof ReverseBuildTrigger && Items 72 | .fromNameList(project.getParent(), 73 | ((ReverseBuildTrigger) trigger).getUpstreamProjects(), Job.class) 74 | .contains(project)) { 75 | edges.add(new ReverseBuildTriggerEdge(project, (Job) downstream)); 76 | } 77 | } 78 | } 79 | return edges; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/project/ParameterizedTriggerSubProjectProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.project; 24 | 25 | import com.google.common.base.Preconditions; 26 | import hudson.model.Job; 27 | import hudson.model.FreeStyleProject; 28 | import hudson.model.Items; 29 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 30 | import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig; 31 | import hudson.plugins.parameterizedtrigger.TriggerBuilder; 32 | import hudson.tasks.Builder; 33 | import jenkins.model.Jenkins; 34 | 35 | import jakarta.inject.Inject; 36 | import java.util.ArrayList; 37 | import java.util.Collections; 38 | import java.util.List; 39 | 40 | import static hudson.plugins.depgraph_view.model.graph.ProjectNode.node; 41 | 42 | /** 43 | * Provides subprojects given by the TriggerBuilder of the ParameterizedTriggerPlugin 44 | */ 45 | public class ParameterizedTriggerSubProjectProvider implements SubProjectProvider { 46 | 47 | private final boolean isParameterizedTriggerPluginInstalled; 48 | 49 | @Inject 50 | public ParameterizedTriggerSubProjectProvider(Jenkins jenkins) { 51 | isParameterizedTriggerPluginInstalled = jenkins.getPlugin("parameterized-trigger") != null; 52 | } 53 | 54 | @Override 55 | public Iterable getSubProjectsOf(Job project) { 56 | Preconditions.checkNotNull(project); 57 | if (!isParameterizedTriggerPluginInstalled) { 58 | return Collections.emptyList(); 59 | } 60 | 61 | List subProjects = new ArrayList(); 62 | if(project instanceof FreeStyleProject) { 63 | FreeStyleProject proj = (FreeStyleProject) project; 64 | List builders = proj.getBuilders(); 65 | for (Builder builder : builders) { 66 | if (builder instanceof TriggerBuilder) { 67 | TriggerBuilder tBuilder = (TriggerBuilder) builder; 68 | for (BlockableBuildTriggerConfig config : tBuilder.getConfigs()) { 69 | for (Job job : Items.fromNameList(project.getParent(), config.getProjects(), Job.class)) { 70 | subProjects.add(node(job) ); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | return subProjects; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/graph/project/SubProjectProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph.project; 24 | 25 | import hudson.ExtensionPoint; 26 | import hudson.model.Job; 27 | import hudson.plugins.depgraph_view.model.graph.DependencyGraphModule; 28 | import hudson.plugins.depgraph_view.model.graph.ProjectNode; 29 | 30 | /** 31 | * This is an extension point which makes it possible to subprojects 32 | * to the DependencyGraph which gets drawn. Note that in order to add your own 33 | * EdgeProvider you must not annotate the corresponding subclass with {@link hudson.Extension} 34 | * but instead add a {@link com.google.inject.Module} with a {@link com.google.inject.multibindings.Multibinder} 35 | * which has the {@link hudson.Extension} annotation. For example see {@link DependencyGraphModule} 36 | * and {@link ParameterizedTriggerSubProjectProvider} 37 | */ 38 | public interface SubProjectProvider extends ExtensionPoint { 39 | public Iterable getSubProjectsOf(Job project); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/operations/DeleteEdgeOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.operations; 24 | 25 | import hudson.model.Result; 26 | import hudson.tasks.BuildTrigger; 27 | import jenkins.model.Jenkins; 28 | 29 | import java.io.IOException; 30 | 31 | public class DeleteEdgeOperation extends EdgeOperation { 32 | 33 | public DeleteEdgeOperation(String sourceJobName, String targetJobName) { 34 | super(sourceJobName, targetJobName); 35 | } 36 | 37 | public void perform() throws IOException { 38 | if (source != null && target != null) { 39 | final BuildTrigger buildTrigger = source.getPublishersList().get(BuildTrigger.class); 40 | if (buildTrigger != null) { 41 | final String childProjectsValue = normalizeChildProjectValue(buildTrigger.getChildProjectsValue().replace(target.getName(), "")); 42 | final Result threshold = buildTrigger.getThreshold(); 43 | source.getPublishersList().remove(buildTrigger); 44 | if (!childProjectsValue.isEmpty()) { 45 | source.getPublishersList().add(new BuildTrigger(childProjectsValue, threshold)); 46 | } 47 | source.save(); 48 | target.save(); 49 | Jenkins.get().rebuildDependencyGraph(); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/operations/EdgeOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.operations; 24 | 25 | import java.io.IOException; 26 | 27 | import org.acegisecurity.AccessDeniedException; 28 | 29 | import hudson.model.AbstractProject; 30 | import hudson.security.Permission; 31 | import jenkins.model.Jenkins; 32 | 33 | public abstract class EdgeOperation { 34 | protected final AbstractProject source; 35 | protected final AbstractProject target; 36 | 37 | public EdgeOperation(String sourceJobName, String targetJobName) { 38 | source = Jenkins.get().getItemByFullName(sourceJobName.trim(), AbstractProject.class); 39 | target = Jenkins.get().getItemByFullName(targetJobName, AbstractProject.class); 40 | if (source == null) { throw new UnsupportedOperationException("editing not supported for upstream job type."); } 41 | if (target == null) { throw new UnsupportedOperationException("editing not supported for downstream job type."); } 42 | if (!source.hasPermission(Permission.CONFIGURE)) { 43 | throw new AccessDeniedException("no permission to configure upstream job."); 44 | } 45 | } 46 | 47 | /** 48 | * Removes double commas and also trailing and leading commas. 49 | * @param actualValue the actual value to be normalized 50 | * @return the value with no unrequired commas 51 | */ 52 | public static String normalizeChildProjectValue(String actualValue){ 53 | actualValue = actualValue.replaceAll("(,[ ]*,)", ", "); 54 | actualValue = actualValue.replaceAll("(^,|,$)", ""); 55 | return actualValue.trim(); 56 | } 57 | 58 | public abstract void perform() throws IOException; 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/hudson/plugins/depgraph_view/model/operations/PutEdgeOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.operations; 24 | 25 | import hudson.tasks.BuildTrigger; 26 | import jenkins.model.Jenkins; 27 | 28 | import java.io.IOException; 29 | 30 | public class PutEdgeOperation extends EdgeOperation { 31 | 32 | public PutEdgeOperation(String sourceJobName, String targetJobName) { 33 | super(sourceJobName, targetJobName); 34 | } 35 | 36 | public void perform() throws IOException { 37 | if (source != null && target != null) { 38 | final BuildTrigger buildTrigger = source.getPublishersList().get(BuildTrigger.class); 39 | if (buildTrigger == null) { 40 | source.getPublishersList().add(new BuildTrigger(target.getName(), true)); 41 | } else { 42 | final String childProjectsValue = normalizeChildProjectValue(buildTrigger.getChildProjectsValue() + ", " + target.getName()); 43 | source.getPublishersList().remove(buildTrigger); 44 | source.getPublishersList().add(new BuildTrigger(childProjectsValue, true)); 45 | } 46 | source.save(); 47 | target.save(); 48 | Jenkins.get().rebuildDependencyGraph(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/AbstractDependencyGraphAction/index.jelly: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |

${it.title}

42 | 43 | 44 | 45 | 46 | 47 |

48 | ${%Graph in graphviz format} 49 |

50 | ${%Legend} 51 |

52 |
53 |
54 |
55 | 56 | 57 | 58 |
59 |
60 | -------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/AbstractDependencyGraphAction/index_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/depgraph-view-plugin/aef4b5ff552257e3914716351f14b3f1af7d1545/src/main/resources/hudson/plugins/depgraph_view/AbstractDependencyGraphAction/index_de.properties -------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/AbstractDependencyGraphAction/jsplumb.jelly: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |

${it.title}

43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 |

52 | ${%Json format} 53 |

54 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 67 | 68 |
69 |
70 |
71 | -------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/AbstractDependencyGraphAction/sidepanel.jelly: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/global.jelly: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/global.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2012 Dominik Bartholdi 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | editFunctionInJSViewEnabled=Enable edit functiononality in jsplumb view 24 | projectNameStripRegex=Strip the project names 25 | graphRankDirection=Graphviz rank direction -------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/global_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/depgraph-view-plugin/aef4b5ff552257e3914716351f14b3f1af7d1545/src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/global_de.properties -------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/help-dotExe.html: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | Configure the location of the dot binary. If not set, defaults to dot (or dot.exe on Windows). 25 |
-------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/help-dotExe_de.html: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | Konfiguriation des Orts des dot Befehls. Wenn nichts gesetzt ist, dann wird 25 | als Standard dot (oder dot.exe bei Windows) verwendet. 26 |
-------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/help-editFunctionInJSViewEnabled.html: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | The graph rendered with jsplumb also allows creation and deletion of dependencies between the jobs via drag and drop. This functionality might not be requested in every setup, therefore one is able to disable it. 25 |
-------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/help-graphRankDirection.html: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | The direction used by graphviz to layout graph nodes.
25 | Can be set to TB (top to bottom) or LR (left to right), default is TB. 26 |
-------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/help-projectNameStripRegex.html: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | Some companies have naming conventions, this often results in long names which take to much space when displayed. This setting lets you define a global regular expression to define what part of the full name should be shown in the graphs. Default is .*, which shows the full name.
25 | com.comp.proj.(.*) will strip 'com.comp.proj' from every project name.
26 | With the group definition one can define the group of the given regex to be used. 27 |
-------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/help-projectNameStripRegexGroup.html: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | Allows to define the group of regular expression to be used for to display as the job name, defaults to 1. 25 |
-------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/help-projectNameStripRegexGroup_de.html: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | Erlaubt es zu definieren welche Gruppe aus der RegularExpression als Job Name im DependencyGraph angezeigt werden soll, Defaults ist 1. 25 |
-------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/help-projectNameStripRegex_de.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/depgraph-view-plugin/aef4b5ff552257e3914716351f14b3f1af7d1545/src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/help-projectNameStripRegex_de.html -------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/DependencyGraphProperty/help-projectNameSuperscriptRegexGroup.html: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | Allows to define the group of regular expression to be used for to display as small caps superscript over the job name, defaults to -1 (disabled). 25 |
-------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/Messages.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2010 Stefan Wolf 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | AbstractDependencyGraphAction.DependencyGraphOf=Dependency Graph of {0} 24 | AbstractDependencyGraphAction.DependencyGraph=Dependency Graph 25 | DependencyGraphProperty.DependencyGraphViewer=Dependency Graph Viewer 26 | DependencyGraphProperty.ProjectNameStripRegex.Required=A Regular Expression is required 27 | DependencyGraphProperty.ProjectNameStripRegex.Invalid=The given expression is not valid -------------------------------------------------------------------------------- /src/main/resources/hudson/plugins/depgraph_view/Messages_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/depgraph-view-plugin/aef4b5ff552257e3914716351f14b3f1af7d1545/src/main/resources/hudson/plugins/depgraph_view/Messages_de.properties -------------------------------------------------------------------------------- /src/main/resources/index.jelly: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | 30 | 31 |
32 | This plugin shows a dependency graph of the projects. 33 | It uses dot (from graphviz) or jsPlumb for drawing. 34 |
35 | -------------------------------------------------------------------------------- /src/main/webapp/css/depview.css: -------------------------------------------------------------------------------- 1 | #paper { 2 | position: relative; 3 | top: 0px; 4 | left: 0px; 5 | height: 1000px; 6 | width: auto; 7 | overflow: scroll 8 | } 9 | 10 | .window { 11 | border: 1px solid #346789; 12 | box-shadow: 2px 2px 19px #aaa; 13 | -o-box-shadow: 2px 2px 19px #aaa; 14 | -webkit-box-shadow: 2px 2px 19px #aaa; 15 | -moz-box-shadow: 2px 2px 19px #aaa; 16 | -moz-border-radius: 0.5em; 17 | border-radius: 0.5em; 18 | opacity: 0.8; 19 | filter: alpha(opacity = 80); 20 | width: auto; 21 | overflow: auto; 22 | min-width: 6em; 23 | max-width: 14em; 24 | min-height: 4em; 25 | height: auto; 26 | line-height: 4em; 27 | text-align: center; 28 | z-index: 20; 29 | position: absolute; 30 | background-color: #eeeeef; 31 | color: black; 32 | font-family: helvetica; 33 | padding: 0.5em 1em; 34 | font-size: 0.9em; 35 | white-space: nowrap; 36 | } 37 | 38 | .window:hover { 39 | box-shadow: 2px 2px 19px #444; 40 | -o-box-shadow: 2px 2px 19px #444; 41 | -webkit-box-shadow: 2px 2px 19px #444; 42 | -moz-box-shadow: 2px 2px 19px #444; 43 | opacity: 0.6; 44 | filter: alpha(opacity = 60); 45 | } 46 | 47 | .ep { 48 | float:right; 49 | width:1em; 50 | height:1em; 51 | background-color:#994466; 52 | cursor:pointer; 53 | display: block; 54 | } 55 | 56 | .active { 57 | border: 1px dotted green; 58 | } 59 | 60 | .hover { 61 | border: 1px dotted red; 62 | } 63 | 64 | ._jsPlumb_connector { 65 | z-index: 4; 66 | } 67 | 68 | ._jsPlumb_endpoint,.endpointTargetLabel,.endpointSourceLabel { 69 | z-index: 21; 70 | cursor: pointer; 71 | } 72 | 73 | .hl { 74 | border: 3px solid red; 75 | } 76 | 77 | #debug { 78 | position: absolute; 79 | background-color: black; 80 | color: red; 81 | z-index: 5000 82 | } 83 | 84 | .aLabel { 85 | background-color: white; 86 | padding: 0.4em; 87 | font: 12px sans-serif; 88 | color: #444; 89 | z-index: 21; 90 | border: 1px dotted gray; 91 | opacity: 0.8; 92 | filter: alpha(opacity = 80); 93 | } -------------------------------------------------------------------------------- /src/main/webapp/css/graph.css: -------------------------------------------------------------------------------- 1 | #canvas 2 | { 3 | width: 400px; 4 | height: 300px 5 | } 6 | -------------------------------------------------------------------------------- /src/main/webapp/help-globalConfig.html: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 |

25 | Put the name of the dot executable from the graphviz project here. 26 |

27 |
28 | -------------------------------------------------------------------------------- /src/main/webapp/js/jsPlumb_depview.js: -------------------------------------------------------------------------------- 1 | ; 2 | (function() { 3 | function escapeId(id) { 4 | // replace all characters except numbers and letters 5 | // As soon as someone opens a bug that they have a problem with a name conflict 6 | // rethink the strategy 7 | return id.replace(/[^a-zA-Z0-9]/g,'-'); 8 | } 9 | 10 | function stripName(name) { 11 | if(window.depview.projectNameStripRegex) { 12 | match = window.depview.projectNameStripRegex.exec(name); 13 | if(match && match[window.depview.projectNameStripRegexGroup]){ 14 | return match[window.depview.projectNameStripRegexGroup]; 15 | } 16 | return name; 17 | } 18 | } 19 | 20 | function getJobDiv(jobName) { 21 | return jQuery('#' + escapeId(jobName)); 22 | } 23 | 24 | window.depview = { 25 | paper: jQuery("#paper"), 26 | colordep: '#FF0000', // red 27 | colorcopy: '#32CD32', // green 28 | init : function() { 29 | jsPlumb.importDefaults({ 30 | Connector : ["StateMachine", { curviness: 10 }],// Straight, Flowchart, Straight, Bezier 31 | // default to blue at one end and green at the other 32 | EndpointStyles : [ { 33 | fillStyle : '#225588' 34 | }, { 35 | fillStyle : '#558822' 36 | } ], 37 | // blue endpoints 7px; green endpoints 7px. 38 | Endpoints : [ [ "Dot", { 39 | radius : 5 40 | } ], [ "Dot", { 41 | radius : 5 42 | } ] ], 43 | Anchors : [ "Continuous", "Continuous" ], 44 | // def for new connector (drag n' drop) 45 | // - line 2px 46 | PaintStyle : { 47 | lineWidth : 2, 48 | strokeStyle : window.depview.colordep, 49 | joinstyle:"round"}, 50 | // the overlays to decorate each connection with. note that the 51 | // label overlay uses a function to generate the label text; in 52 | // this case it returns the 'labelText' member that we set on each 53 | // connection in the 'init' method below. 54 | ConnectionOverlays : [ [ "Arrow", { 55 | location : 1.0, 56 | foldback:0.5 57 | } ] ], 58 | ConnectionsDetachable:false 59 | }); 60 | jQuery.getJSON('graph.json', function(data) { 61 | 62 | var top = 3; 63 | var space = 150; 64 | var xOverall = 90; 65 | 66 | var clusters = data["clusters"]; 67 | // iterate clusters 68 | jQuery.each(clusters, function(i, cluster) { 69 | jQuery.each(cluster.nodes, function(i,node) { 70 | var nodeString = '
' 71 | if (window.depview.editEnabled) { 72 | nodeString = nodeString + '
'; 73 | } 74 | nodeString = nodeString + '' + stripName(node.name) + '
' 75 | var jnode = jQuery(nodeString); 76 | var width = jnode.addClass('window'). 77 | attr('id', escapeId(node.name)). 78 | attr('data-jobname', node.fullName). 79 | css('top', node.y + top). 80 | css('left', node.x). 81 | appendTo(window.depview.paper).width(); 82 | jnode.css('left', node.x - width/2 + xOverall); 83 | }) 84 | top = top + cluster.vSize + space 85 | }); 86 | // definitions for drag/drop connections 87 | jQuery(".ep").each(function(idx, current) { 88 | var p = jQuery(current).parent() 89 | if(window.depview.editEnabled) { 90 | jsPlumb.makeSource(current, { 91 | parent: p, 92 | scope: "dep" 93 | }); 94 | } 95 | }) 96 | jsPlumb.makeTarget(jsPlumb.getSelector('.window'), {scope: "dep"}); 97 | 98 | var edges = data["edges"]; 99 | jQuery.each(edges, function(i, edge) { 100 | from = getJobDiv(edge["from"]); 101 | to = getJobDiv(edge["to"]); 102 | // creates/defines the look and feel of the loaded connections: red="dep", green="copy" 103 | var connection; 104 | var connOptions = { 105 | source : from, 106 | target : to, 107 | scope: edge["type"], 108 | paintStyle:{lineWidth : 2} 109 | } 110 | if("copy" == edge["type"]){ 111 | connOptions.paintStyle.strokeStyle = window.depview.colorcopy; 112 | connOptions.overlays = [[ "Label", { label: "copy", id: from+'.'+to } ]]; 113 | connection = jsPlumb.connect(connOptions); 114 | } else { 115 | connOptions.paintStyle.strokeStyle = window.depview.colordep; 116 | connection = jsPlumb.connect(connOptions); 117 | // only allow deletion of "dep" connections 118 | if(window.depview.editEnabled) { 119 | connection.bind("click", function(conn) { 120 | var sourceJobName = conn.source.attr('data-jobname'); 121 | var targetJobName = conn.target.attr('data-jobname') 122 | jQuery.ajax({ 123 | url : encodeURI('edge/' + sourceJobName + '/' + targetJobName), 124 | type : 'DELETE', 125 | success : function(response) { 126 | jsPlumb.detach(conn); 127 | }, 128 | error: function (request, status, error) { 129 | alert(status+": "+error); 130 | } 131 | }); 132 | }); 133 | } 134 | } 135 | }); 136 | 137 | if(window.depview.editEnabled) { 138 | jsPlumb.bind("jsPlumbConnection", function(info) { 139 | var connections = jsPlumb.getConnections({ scope: "dep", source: info.sourceId, target: info.targetId }); 140 | if ((info.sourceId == info.targetId) || connections.length > 1) { 141 | jsPlumb.detach(info); 142 | return; 143 | } 144 | jQuery.ajax({ 145 | url: encodeURI('edge/'+info.source.attr('data-jobname') +'/'+info.target.attr('data-jobname')), 146 | type: 'PUT', 147 | success: function( response ) { 148 | // alert('Load was performed.'); 149 | }, 150 | error: function (request, status, error) { 151 | alert(status+": "+error); 152 | jsPlumb.detach(info); 153 | } 154 | }); 155 | // allow deletion of newly created connection 156 | info.connection.bind("click", function(conn) { 157 | var sourceJobName = conn.source.attr('data-jobname'); 158 | var targetJobName = conn.target.attr('data-jobname'); 159 | jQuery.ajax({ 160 | url : encodeURI('edge/' + sourceJobName + '/' + targetJobName), 161 | type : 'DELETE', 162 | success : function(response) { 163 | jsPlumb.detach(conn); 164 | }, 165 | error: function (request, status, error) { 166 | alert(status+": "+error); 167 | } 168 | }); 169 | }); 170 | }); 171 | } 172 | 173 | // make all the window divs draggable 174 | jsPlumb.draggable(jsPlumb.getSelector(".window")); 175 | }); 176 | } 177 | }; 178 | })(); 179 | 180 | // start jsPlumb 181 | jsPlumb.bind("ready", function() { 182 | // chrome fix. 183 | document.onselectstart = function () { return false; }; 184 | 185 | jsPlumb.setRenderMode(jsPlumb.SVG); 186 | depview.init(); 187 | }); 188 | 189 | -------------------------------------------------------------------------------- /src/test/java/hudson/plugins/depgraph_view/model/graph/GraphCalculatorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Stefan Wolf 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package hudson.plugins.depgraph_view.model.graph; 24 | 25 | import com.google.common.collect.ImmutableSet; 26 | import com.google.common.collect.Lists; 27 | import hudson.model.Job; 28 | import hudson.plugins.depgraph_view.model.graph.edge.DependencyGraphEdgeProvider; 29 | import hudson.plugins.depgraph_view.model.graph.edge.EdgeProvider; 30 | import hudson.model.FreeStyleProject; 31 | import hudson.tasks.BuildTrigger; 32 | import org.junit.Rule; 33 | import org.junit.Test; 34 | import org.jvnet.hudson.test.JenkinsRule; 35 | 36 | import java.io.IOException; 37 | import java.util.Arrays; 38 | import java.util.Collection; 39 | import java.util.Collections; 40 | 41 | import static hudson.plugins.depgraph_view.model.graph.ProjectNode.node; 42 | import static org.junit.Assert.assertTrue; 43 | 44 | public class GraphCalculatorTest { 45 | private FreeStyleProject project1; 46 | private FreeStyleProject project2; 47 | 48 | @Rule 49 | public JenkinsRule j = new JenkinsRule(); 50 | 51 | @Test 52 | public void testCalculateDepsDownstream() throws IOException { 53 | createProjects(); 54 | addDependency(project1, project2); 55 | j.jenkins.rebuildDependencyGraph(); 56 | DependencyGraph graph = generateGraph(project1); 57 | assertGraphContainsProjects(graph, project1, project2); 58 | assertTrue(graph.findEdgeSet(node(project1), node(project2)).size() == 1); 59 | } 60 | 61 | private void assertGraphContainsProjects(DependencyGraph graph, Job... projects) { 62 | Collection projectNodes = Lists.newArrayList(GraphCalculator.jobSetToProjectNodeSet(Arrays.asList(projects))); 63 | assertTrue(graph.getNodes().containsAll(projectNodes)); 64 | assertTrue(graph.getNodes().size() == projectNodes.size()); 65 | } 66 | 67 | private DependencyGraph generateGraph(Job from) { 68 | return new GraphCalculator(getDependencyGraphEdgeProviders()).generateGraph(Collections.singleton(node(from))); 69 | } 70 | 71 | @Test 72 | public void testCalculateDepsUpstream() throws IOException { 73 | createProjects(); 74 | addDependency(project1, project2); 75 | j.getInstance().rebuildDependencyGraph(); 76 | DependencyGraph graph = generateGraph(project2); 77 | assertGraphContainsProjects(graph, project1, project2); 78 | assertTrue(graph.findEdgeSet(node(project1), node(project2)).size() == 1); 79 | } 80 | 81 | private void assertHasOneDependencyEdge(DependencyGraph graph, Job from, Job to) { 82 | assertTrue(graph.findEdgeSet(node(from), node(to)).size() == 1); 83 | } 84 | 85 | @Test 86 | public void testCalculateDepsTransitive() throws IOException { 87 | createProjects(); 88 | FreeStyleProject project3 = j.createFreeStyleProject(); 89 | addDependency(project2, project1); 90 | addDependency(project2, project3); 91 | j.getInstance().rebuildDependencyGraph(); 92 | 93 | DependencyGraph graph = generateGraph(project1); 94 | assertGraphContainsProjects(graph, project1, project2); 95 | assertHasOneDependencyEdge(graph, project2, project1); 96 | 97 | graph = generateGraph(project2); 98 | assertGraphContainsProjects(graph, project1, project2, project3); 99 | assertHasOneDependencyEdge(graph, project2, project1); 100 | assertHasOneDependencyEdge(graph, project2, project3); 101 | } 102 | 103 | private void createProjects() throws IOException { 104 | project1 = j.createFreeStyleProject(); 105 | project2 = j.createFreeStyleProject(); 106 | } 107 | 108 | private ImmutableSet getDependencyGraphEdgeProviders() { 109 | return ImmutableSet.of(new DependencyGraphEdgeProvider(j.getInstance())); 110 | } 111 | 112 | private void addDependency(FreeStyleProject project1, FreeStyleProject project2) throws IOException { 113 | project1.getPublishersList().add(new BuildTrigger(project2.getName(), false)); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/test/java/hudson/plugins/depgraph_view/model/graph/NormalizeTest.java: -------------------------------------------------------------------------------- 1 | package hudson.plugins.depgraph_view.model.graph; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import hudson.plugins.depgraph_view.model.operations.EdgeOperation; 6 | 7 | import org.junit.Test; 8 | 9 | public class NormalizeTest { 10 | 11 | @Test 12 | public void test() { 13 | String childProjectsValue = "abc, ,ccc"; 14 | childProjectsValue = EdgeOperation.normalizeChildProjectValue(childProjectsValue); 15 | assertEquals("abc, ccc", childProjectsValue); 16 | } 17 | @Test 18 | public void test1() { 19 | String childProjectsValue = "abc, ,ccc"; 20 | childProjectsValue = EdgeOperation.normalizeChildProjectValue(childProjectsValue); 21 | assertEquals("abc, ccc", childProjectsValue); 22 | } 23 | @Test 24 | public void test2() { 25 | String childProjectsValue = "abc, ccc"; 26 | childProjectsValue = EdgeOperation.normalizeChildProjectValue(childProjectsValue); 27 | assertEquals("abc, ccc", childProjectsValue); 28 | } 29 | 30 | @Test 31 | public void test3() { 32 | String childProjectsValue = ",abc, ,ccc,"; 33 | childProjectsValue = EdgeOperation.normalizeChildProjectValue(childProjectsValue); 34 | assertEquals("abc, ccc", childProjectsValue); 35 | } 36 | 37 | @Test 38 | public void test4() { 39 | String childProjectsValue = ",abc,,ccc,"; 40 | childProjectsValue = EdgeOperation.normalizeChildProjectValue(childProjectsValue); 41 | assertEquals("abc, ccc", childProjectsValue); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/hudson/plugins/depgraph_view/model/graph/StripProjectNameTest.java: -------------------------------------------------------------------------------- 1 | package hudson.plugins.depgraph_view.model.graph; 2 | 3 | import static hudson.plugins.depgraph_view.model.graph.ProjectNode.node; 4 | import static org.junit.Assert.assertTrue; 5 | import hudson.model.AbstractProject; 6 | import hudson.model.FreeStyleProject; 7 | import hudson.plugins.depgraph_view.DependencyGraphProperty.DescriptorImpl; 8 | import hudson.plugins.depgraph_view.model.graph.edge.DependencyGraphEdgeProvider; 9 | import hudson.plugins.depgraph_view.model.graph.edge.EdgeProvider; 10 | import hudson.plugins.depgraph_view.model.graph.project.SubProjectProvider; 11 | import hudson.plugins.depgraph_view.model.display.DotStringGenerator; 12 | 13 | import java.util.Collections; 14 | 15 | import jenkins.model.JenkinsLocationConfiguration; 16 | 17 | import org.junit.Rule; 18 | import org.junit.Test; 19 | import org.jvnet.hudson.test.JenkinsRule; 20 | 21 | import com.google.common.collect.ImmutableSet; 22 | import com.google.common.collect.ListMultimap; 23 | 24 | public class StripProjectNameTest { 25 | 26 | @Rule 27 | public JenkinsRule j = new JenkinsRule(); 28 | 29 | @Test 30 | public void projectNameShouldBeStriped() throws Exception { 31 | JenkinsLocationConfiguration.get().setUrl("http://localhost"); 32 | j.getInstance().getDescriptorByType(DescriptorImpl.class).setProjectNameStripRegex("com.comp.(.*)"); 33 | j.getInstance().getDescriptorByType(DescriptorImpl.class).setProjectNameStripRegexGroup(1); 34 | 35 | final FreeStyleProject myJob = j.createFreeStyleProject("com.comp.myJob"); 36 | j.getInstance().rebuildDependencyGraph(); 37 | DependencyGraph graph = generateGraph(myJob); 38 | final SubprojectCalculator subprojectCalculator = new SubprojectCalculator(Collections.emptySet()); 39 | final ListMultimap subProjects = subprojectCalculator.generate(graph); 40 | final DotStringGenerator dotStringGenerator = new DotStringGenerator(j.getInstance(), graph, subProjects); 41 | final String dotString = dotStringGenerator.generate(); 42 | System.err.println(dotString); 43 | // we can't test for not existing of the original name, because that one is still required for linking, 44 | // so we explicitly check for the short/striped version only 45 | assertTrue("node label should contain stripped job name", dotString.contains(">myJob<")); 46 | 47 | } 48 | 49 | @Test 50 | public void projectNameShouldBeRelabeled() throws Exception { 51 | JenkinsLocationConfiguration.get().setUrl("http://localhost"); 52 | j.getInstance().getDescriptorByType(DescriptorImpl.class).setProjectNameStripRegex("(com[.]comp[.])(.*)"); 53 | j.getInstance().getDescriptorByType(DescriptorImpl.class).setProjectNameStripRegexGroup(2); 54 | j.getInstance().getDescriptorByType(DescriptorImpl.class).setProjectNameSuperscriptRegexGroup(1); 55 | 56 | final FreeStyleProject myJob = j.createFreeStyleProject("com.comp.myJob"); 57 | j.getInstance().rebuildDependencyGraph(); 58 | DependencyGraph graph = generateGraph(myJob); 59 | final SubprojectCalculator subprojectCalculator = new SubprojectCalculator(Collections.emptySet()); 60 | final ListMultimap subProjects = subprojectCalculator.generate(graph); 61 | final DotStringGenerator dotStringGenerator = new DotStringGenerator(j.getInstance(), graph, subProjects); 62 | final String dotString = dotStringGenerator.generate(); 63 | System.err.println(dotString); 64 | // we can't test for not existing of the original name, because that one is still required for linking, 65 | // so we explicitly check for the short/striped version only 66 | assertTrue("node label should contain superscript and stripped name", dotString.contains("com.comp.
myJob")); 67 | 68 | } 69 | 70 | private DependencyGraph generateGraph(AbstractProject from) { 71 | return new GraphCalculator(getDependencyGraphEdgeProviders()).generateGraph(Collections.singleton(node(from))); 72 | } 73 | 74 | private ImmutableSet getDependencyGraphEdgeProviders() { 75 | return ImmutableSet.of(new DependencyGraphEdgeProvider(j.getInstance())); 76 | } 77 | } 78 | --------------------------------------------------------------------------------