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.servicemix.jbi.view;
018    
019    import java.io.PrintWriter;
020    import java.util.ArrayList;
021    import java.util.Collection;
022    import java.util.List;
023    import java.util.Map;
024    import java.util.Set;
025    import java.util.concurrent.ConcurrentHashMap;
026    import java.util.concurrent.CopyOnWriteArraySet;
027    
028    import javax.jbi.messaging.MessageExchange;
029    import javax.jbi.servicedesc.ServiceEndpoint;
030    
031    import org.apache.servicemix.JbiConstants;
032    import org.apache.servicemix.jbi.event.ComponentEvent;
033    import org.apache.servicemix.jbi.event.ComponentListener;
034    import org.apache.servicemix.jbi.event.EndpointEvent;
035    import org.apache.servicemix.jbi.event.ExchangeEvent;
036    import org.apache.servicemix.jbi.event.ExchangeListener;
037    import org.apache.servicemix.jbi.framework.ComponentMBeanImpl;
038    import org.apache.servicemix.jbi.framework.Registry;
039    import org.apache.servicemix.jbi.messaging.MessageExchangeImpl;
040    import org.apache.servicemix.jbi.servicedesc.AbstractServiceEndpoint;
041    import org.apache.servicemix.jbi.servicedesc.EndpointSupport;
042    
043    /**
044     * Creates a <a href="http://www.graphviz.org/">DOT</a> file showing the JBI MessageExchange
045     * flow within the JBI Container.
046    
047     * @org.apache.xbean.XBean 
048     * description="Generates DOT visualisations of the exchanges flow inside ServiceMix"
049     * 
050     * @version $Revision: 391707 $
051     */
052    public class DotViewFlowListener extends DotViewEndpointListener 
053        implements ExchangeListener, ComponentListener {
054    
055        private Map<String, Map<String, Boolean>> flow;
056        private Set<String> flowLinks;
057        private Set<String> usedComponents;
058        private Set<String> componentsAsConsumer;
059        private boolean displayComponents = true;
060        
061        public DotViewFlowListener() {
062            setFile("ServiceMixFlow.dot");
063            flow = new ConcurrentHashMap<String, Map<String, Boolean>>();
064            flowLinks = new CopyOnWriteArraySet<String>();
065            usedComponents = new CopyOnWriteArraySet<String>();
066            componentsAsConsumer = new CopyOnWriteArraySet<String>();
067        }
068    
069        // Implementation methods
070        // -------------------------------------------------------------------------
071    
072        protected void generateFile(PrintWriter writer) throws Exception {
073            writer.println("digraph \"Apache ServiceMix\" {");
074            writer.println();
075            writer.println("label = \"Apache ServiceMix flow\";");
076            writer.println("node [ shape = box, style = \"rounded,filled\", fontname = \"Helvetica-Oblique\", fontsize = 8 ];");
077            writer.println();
078    
079            List<String> brokerLinks = new ArrayList<String>();
080            Registry registry = getContainer().getRegistry();
081            Collection<ComponentMBeanImpl> components = registry.getComponents();
082            for (ComponentMBeanImpl component : components) {
083                ServiceEndpoint[] ses = registry.getEndpointRegistry().getAllEndpointsForComponent(component.getComponentNameSpace());
084                String name = component.getName();
085                if (!usedComponents.contains(name)) {
086                    continue;
087                }
088                // If we want to display components, create
089                // a sub graph, grouping all the components
090                // endpoints
091                if (isDisplayComponents()) {
092                    String id = encode(name);
093                    writer.println("subgraph cluster_" + id + " {");
094                    writer.println("  label=\"" + name + "\";");
095                    if (componentsAsConsumer.contains(name)) {
096                        writer.println("  " + id + " [ fillcolor = gray, label = \"" + name + "\" ];");
097                    }
098                }
099                for (int i = 0; i < ses.length; i++) {
100                    String key = EndpointSupport.getUniqueKey(ses[i]);
101                    String epname = formatEndpoint(key);
102                    if (!isDisplayComponents()) {
103                        epname += "\\n" + name;
104                    }
105                    String color = "lightgray";
106                    if (epname.startsWith("internal")) {
107                        epname = epname.substring(10);
108                        color = "#6699ff";
109                    } else if (epname.startsWith("external")) {
110                        epname = epname.substring(10);
111                        color = "#66ccff";
112                    } else if (epname.startsWith("dynamic")) {
113                        epname = epname.substring(9);
114                        color = "#6666ff";
115                    } else if (epname.startsWith("linked")) {
116                        epname = epname.substring(8);
117                        color = "#66ffff";
118                    } else {
119                        color = "#f3f3f3";
120                    }
121                    String epid = encode(key);
122                    writer.println("  " + epid + " [fillcolor = \"" + color + "\", label=\"" + epname + "\"];");
123                }
124                if (isDisplayComponents()) {
125                    writer.println("}");
126                }
127            }
128            writer.println();
129            generateLinks(writer, brokerLinks);
130    
131            writer.println();
132    
133            generateLinks(writer, flowLinks);
134            
135            writer.println("}");
136        }
137        
138        public void exchangeSent(ExchangeEvent event) {
139            MessageExchange me = event.getExchange();
140            if (me.getEndpoint() instanceof AbstractServiceEndpoint && me instanceof MessageExchangeImpl) {
141                MessageExchangeImpl mei = (MessageExchangeImpl) me;
142                String source = (String) me.getProperty(JbiConstants.SENDER_ENDPOINT);
143                if (source == null) {
144                    source = mei.getSourceId().getName();
145                    componentsAsConsumer.add(source);
146                } else {
147                    ServiceEndpoint[] ses = getContainer().getRegistry().getEndpointRegistry().getAllEndpointsForComponent(mei.getSourceId());
148                    for (int i = 0; i < ses.length; i++) {
149                        if (EndpointSupport.getKey(ses[i]).equals(source)) {
150                            source = EndpointSupport.getUniqueKey(ses[i]);
151                            break;
152                        }
153                    }
154                }
155                usedComponents.add(mei.getSourceId().getName());
156                if (((AbstractServiceEndpoint) mei.getEndpoint()).getComponentNameSpace() != null) {
157                    usedComponents.add(((AbstractServiceEndpoint) mei.getEndpoint()).getComponentNameSpace().getName());
158                }
159                String dest = EndpointSupport.getUniqueKey(mei.getEndpoint());
160                Map<String, Boolean> componentFlow = createSource(source);
161                if (componentFlow.put(dest, Boolean.TRUE) == null) {
162                    flowLinks.add(encode(source) + " -> " + encode(dest));
163                    viewIsDirty(mei.getEndpoint());
164                }
165            }
166        }
167        
168        public void exchangeAccepted(ExchangeEvent event) {
169        }
170        
171        protected Map<String, Boolean> createSource(String name) {
172            synchronized (flow) {
173                Map<String, Boolean> componentFlow = flow.get(name);
174                if (componentFlow == null) {
175                    componentFlow = new ConcurrentHashMap<String, Boolean>();
176                    flow.put(name, componentFlow);
177                }
178                return componentFlow;
179            }
180        }
181    
182        public void internalEndpointRegistered(EndpointEvent event) {
183            String key = EndpointSupport.getUniqueKey(event.getEndpoint());
184            createSource(key);
185            super.internalEndpointRegistered(event);
186        }
187        
188        public void externalEndpointRegistered(EndpointEvent event) {
189            String key = EndpointSupport.getUniqueKey(event.getEndpoint());
190            createSource(key);
191            super.externalEndpointRegistered(event);
192        }
193        
194        public void linkedEndpointRegistered(EndpointEvent event) {
195            String key = EndpointSupport.getUniqueKey(event.getEndpoint());
196            createSource(key);
197            super.linkedEndpointRegistered(event);
198        }
199        
200        public void componentInstalled(ComponentEvent event) {
201            createSource(event.getComponent().getName());
202        }
203        
204        public void componentInitialized(ComponentEvent event) {
205            createSource(event.getComponent().getName());
206        }
207    
208        public void componentStarted(ComponentEvent event) {
209        }
210    
211        public void componentStopped(ComponentEvent event) {
212        }
213    
214        public void componentShutDown(ComponentEvent event) {
215        }
216    
217        public void componentUninstalled(ComponentEvent event) {
218        }
219    
220        public boolean isDisplayComponents() {
221            return displayComponents;
222        }
223    
224        public void setDisplayComponents(boolean displayComponents) {
225            this.displayComponents = displayComponents;
226        }
227    
228    }