001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.view; 018 019 import java.io.PrintWriter; 020 import java.util.List; 021 import java.util.Map; 022 import java.util.Set; 023 024 import org.apache.camel.model.FromDefinition; 025 import org.apache.camel.model.MulticastDefinition; 026 import org.apache.camel.model.PipelineDefinition; 027 import org.apache.camel.model.ProcessorDefinition; 028 import org.apache.camel.model.RouteDefinition; 029 import org.apache.camel.model.ToDefinition; 030 031 import static org.apache.camel.util.ObjectHelper.isNotEmpty; 032 /** 033 * A <a href="http://www.graphviz.org/">DOT</a> file creator plugin which 034 * creates a DOT file showing the current routes 035 * 036 * @version $Revision: 767405 $ 037 */ 038 public class RouteDotGenerator extends GraphGeneratorSupport { 039 040 public RouteDotGenerator(String dir) { 041 super(dir, ".dot"); 042 } 043 044 // Implementation methods 045 //------------------------------------------------------------------------- 046 047 protected void printRoutes(PrintWriter writer, Map<String, List<RouteDefinition>> map) { 048 Set<Map.Entry<String, List<RouteDefinition>>> entries = map.entrySet(); 049 for (Map.Entry<String, List<RouteDefinition>> entry : entries) { 050 String group = entry.getKey(); 051 printRoutes(writer, group, entry.getValue()); 052 } 053 } 054 055 protected void printRoutes(PrintWriter writer, String group, List<RouteDefinition> routes) { 056 if (group != null) { 057 writer.println("subgraph cluster_" + (clusterCounter++) + " {"); 058 writer.println("label = \"" + group + "\";"); 059 writer.println("color = grey;"); 060 writer.println("style = \"dashed\";"); 061 writer.println("URL = \"" + group + ".html\";"); 062 writer.println(); 063 } 064 for (RouteDefinition route : routes) { 065 List<FromDefinition> inputs = route.getInputs(); 066 for (FromDefinition input : inputs) { 067 printRoute(writer, route, input); 068 } 069 writer.println(); 070 } 071 if (group != null) { 072 writer.println("}"); 073 writer.println(); 074 } 075 } 076 077 protected String escapeNodeId(String text) { 078 return text.replace('.', '_').replace("$", "_"); 079 } 080 081 protected void printRoute(PrintWriter writer, final RouteDefinition route, FromDefinition input) { 082 NodeData nodeData = getNodeData(input); 083 084 printNode(writer, nodeData); 085 086 // TODO we should add a transactional client / event driven consumer / polling client 087 088 NodeData from = nodeData; 089 for (ProcessorDefinition output : route.getOutputs()) { 090 NodeData newData = printNode(writer, from, output); 091 from = newData; 092 } 093 } 094 095 @SuppressWarnings("unchecked") 096 protected NodeData printNode(PrintWriter writer, NodeData fromData, ProcessorDefinition node) { 097 if (node instanceof MulticastDefinition) { 098 // no need for a multicast or interceptor node 099 List<ProcessorDefinition> outputs = node.getOutputs(); 100 boolean isPipeline = isPipeline(node); 101 for (ProcessorDefinition output : outputs) { 102 NodeData out = printNode(writer, fromData, output); 103 // if in pipeline then we should move the from node to the next in the pipeline 104 if (isPipeline) { 105 fromData = out; 106 } 107 } 108 return fromData; 109 } 110 NodeData toData = getNodeData(node); 111 112 printNode(writer, toData); 113 114 if (fromData != null) { 115 writer.print(fromData.id); 116 writer.print(" -> "); 117 writer.print(toData.id); 118 writer.println(" ["); 119 120 String label = fromData.edgeLabel; 121 if (isNotEmpty(label)) { 122 writer.println("label = \"" + label + "\""); 123 } 124 writer.println("];"); 125 } 126 127 // now lets write any children 128 //List<ProcessorType> outputs = node.getOutputs(); 129 List<ProcessorDefinition> outputs = toData.outputs; 130 if (outputs != null) { 131 for (ProcessorDefinition output : outputs) { 132 NodeData newData = printNode(writer, toData, output); 133 if (!isMulticastNode(node)) { 134 toData = newData; 135 } 136 } 137 } 138 return toData; 139 } 140 141 protected void printNode(PrintWriter writer, NodeData data) { 142 if (!data.nodeWritten) { 143 data.nodeWritten = true; 144 145 writer.println(); 146 writer.print(data.id); 147 writer.println(" ["); 148 writer.println("label = \"" + data.label + "\""); 149 writer.println("tooltip = \"" + data.tooltop + "\""); 150 if (data.url != null) { 151 writer.println("URL = \"" + data.url + "\""); 152 } 153 154 String image = data.image; 155 if (image != null) { 156 writer.println("shapefile = \"" + image + "\""); 157 writer.println("peripheries=0"); 158 } 159 String shape = data.shape; 160 if (shape == null && image != null) { 161 shape = "custom"; 162 } 163 if (shape != null) { 164 writer.println("shape = \"" + shape + "\""); 165 } 166 writer.println("];"); 167 writer.println(); 168 } 169 } 170 171 protected void generateFile(PrintWriter writer, Map<String, List<RouteDefinition>> map) { 172 writer.println("digraph CamelRoutes {"); 173 writer.println(); 174 175 writer.println("node [style = \"rounded,filled\", fillcolor = yellow, " 176 + "fontname=\"Helvetica-Oblique\"];"); 177 writer.println(); 178 printRoutes(writer, map); 179 180 writer.println("}"); 181 } 182 183 /** 184 * Is the given node a pipeline 185 */ 186 private static boolean isPipeline(ProcessorDefinition node) { 187 if (node instanceof MulticastDefinition) { 188 return false; 189 } 190 if (node instanceof PipelineDefinition) { 191 return true; 192 } 193 if (node.getOutputs().size() > 1) { 194 // is pipeline if there is more than 1 output and they are all To types 195 for (Object type : node.getOutputs()) { 196 if (!(type instanceof ToDefinition)) { 197 return false; 198 } 199 } 200 return true; 201 } 202 return false; 203 } 204 205 }