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.activemq.broker.view;
018    
019    import org.apache.activemq.broker.Broker;
020    import org.apache.activemq.broker.ConnectionContext;
021    import org.apache.activemq.broker.ProducerBrokerExchange;
022    import org.apache.activemq.broker.jmx.BrokerViewMBean;
023    import org.apache.activemq.broker.jmx.SubscriptionViewMBean;
024    import org.apache.activemq.broker.region.Subscription;
025    import org.apache.activemq.command.ActiveMQDestination;
026    import org.apache.activemq.command.ConsumerInfo;
027    import org.apache.activemq.command.Message;
028    import org.apache.activemq.command.ProducerId;
029    import org.apache.activemq.command.ProducerInfo;
030    import org.apache.activemq.filter.DestinationMapNode;
031    import java.io.IOException;
032    import java.io.PrintWriter;
033    import java.util.Collection;
034    import java.util.HashMap;
035    import java.util.HashSet;
036    import java.util.Iterator;
037    import java.util.Map;
038    import java.util.Set;
039    import javax.management.ObjectName;
040    
041    /**
042     * 
043     */
044    public class ConnectionDotFileInterceptor extends DotFileInterceptorSupport {
045    
046        protected static final String ID_SEPARATOR = "_";
047    
048        private final boolean redrawOnRemove;
049        private boolean clearProducerCacheAfterRender;
050        private String domain = "org.apache.activemq";
051        private BrokerViewMBean brokerView;
052    
053        // until we have some MBeans for producers, lets do it all ourselves
054        private Map<ProducerId, ProducerInfo> producers = new HashMap<ProducerId, ProducerInfo>();
055        private Map<ProducerId, Set<ActiveMQDestination>> producerDestinations = new HashMap<ProducerId, Set<ActiveMQDestination>>();
056        private Object lock = new Object();
057    
058        public ConnectionDotFileInterceptor(Broker next, String file, boolean redrawOnRemove) throws IOException {
059            super(next, file);
060            this.redrawOnRemove = redrawOnRemove;
061            
062        }
063    
064        public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
065            Subscription answer = super.addConsumer(context, info);
066            generateFile();
067            return answer;
068        }
069    
070        public void addProducer(ConnectionContext context, ProducerInfo info) throws Exception {
071            super.addProducer(context, info);
072            ProducerId producerId = info.getProducerId();
073            synchronized (lock) {
074                producers.put(producerId, info);
075            }
076            generateFile();
077        }
078    
079        public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
080            super.removeConsumer(context, info);
081            if (redrawOnRemove) {
082                generateFile();
083            }
084        }
085    
086        public void removeProducer(ConnectionContext context, ProducerInfo info) throws Exception {
087            super.removeProducer(context, info);
088            ProducerId producerId = info.getProducerId();
089            if (redrawOnRemove) {
090                synchronized (lock) {
091                    producerDestinations.remove(producerId);
092                    producers.remove(producerId);
093                }
094                generateFile();
095            }
096        }
097    
098        public void send(ProducerBrokerExchange producerExchange, Message messageSend) throws Exception {
099            super.send(producerExchange, messageSend);
100            ProducerId producerId = messageSend.getProducerId();
101            ActiveMQDestination destination = messageSend.getDestination();
102            synchronized (lock) {
103                Set<ActiveMQDestination> destinations = producerDestinations.get(producerId);
104                if (destinations == null) {
105                    destinations = new HashSet<ActiveMQDestination>();
106                }
107                producerDestinations.put(producerId, destinations);
108                destinations.add(destination);
109            }
110        }
111    
112        protected void generateFile(PrintWriter writer) throws Exception {
113    
114            writer.println("digraph \"ActiveMQ Connections\" {");
115            writer.println();
116            writer.println("label=\"ActiveMQ Broker: " + getBrokerView().getBrokerId() + "\"];");
117            writer.println();
118            writer.println("node [style = \"rounded,filled\", fillcolor = yellow, fontname=\"Helvetica-Oblique\"];");
119            writer.println();
120    
121            Map<String, String> clients = new HashMap<String, String>();
122            Map<String, String> queues = new HashMap<String, String>();
123            Map<String, String> topics = new HashMap<String, String>();
124    
125            printSubscribers(writer, clients, queues, "queue_", getBrokerView().getQueueSubscribers());
126            writer.println();
127    
128            printSubscribers(writer, clients, topics, "topic_", getBrokerView().getTopicSubscribers());
129            writer.println();
130    
131            printProducers(writer, clients, queues, topics);
132            writer.println();
133    
134            writeLabels(writer, "green", "Client: ", clients);
135            writer.println();
136    
137            writeLabels(writer, "red", "Queue: ", queues);
138            writeLabels(writer, "blue", "Topic: ", topics);
139            writer.println("}");
140    
141            if (clearProducerCacheAfterRender) {
142                producerDestinations.clear();
143            }
144        }
145    
146        protected void printProducers(PrintWriter writer, Map<String, String> clients, Map<String, String> queues, Map<String, String> topics) {
147            synchronized (lock) {
148                for (Iterator iter = producerDestinations.entrySet().iterator(); iter.hasNext();) {
149                    Map.Entry entry = (Map.Entry)iter.next();
150                    ProducerId producerId = (ProducerId)entry.getKey();
151                    Set destinationSet = (Set)entry.getValue();
152                    printProducers(writer, clients, queues, topics, producerId, destinationSet);
153                }
154            }
155        }
156    
157        protected void printProducers(PrintWriter writer, Map<String, String> clients, Map<String, String> queues, Map<String, String> topics, ProducerId producerId, Set destinationSet) {
158            for (Iterator iter = destinationSet.iterator(); iter.hasNext();) {
159                ActiveMQDestination destination = (ActiveMQDestination)iter.next();
160    
161                // TODO use clientId one day
162                String clientId = producerId.getConnectionId();
163                String safeClientId = asID(clientId);
164                clients.put(safeClientId, clientId);
165    
166                String physicalName = destination.getPhysicalName();
167                String safeDestinationId = asID(physicalName);
168                if (destination.isTopic()) {
169                    safeDestinationId = "topic_" + safeDestinationId;
170                    topics.put(safeDestinationId, physicalName);
171                } else {
172                    safeDestinationId = "queue_" + safeDestinationId;
173                    queues.put(safeDestinationId, physicalName);
174                }
175    
176                String safeProducerId = asID(producerId.toString());
177    
178                // lets write out the links
179    
180                writer.print(safeClientId);
181                writer.print(" -> ");
182                writer.print(safeProducerId);
183                writer.println(";");
184    
185                writer.print(safeProducerId);
186                writer.print(" -> ");
187                writer.print(safeDestinationId);
188                writer.println(";");
189    
190                // now lets write out the label
191                writer.print(safeProducerId);
192                writer.print(" [label = \"");
193                String label = "Producer: " + producerId.getSessionId() + "-" + producerId.getValue();
194                writer.print(label);
195                writer.println("\"];");
196    
197            }
198        }
199    
200        protected void printSubscribers(PrintWriter writer, Map<String, String> clients, Map<String, String> destinations, String type, ObjectName[] subscribers) {
201            for (int i = 0; i < subscribers.length; i++) {
202                ObjectName name = subscribers[i];
203                SubscriptionViewMBean subscriber = (SubscriptionViewMBean)getBrokerService().getManagementContext().newProxyInstance(name, SubscriptionViewMBean.class, true);
204    
205                String clientId = subscriber.getClientId();
206                String safeClientId = asID(clientId);
207                clients.put(safeClientId, clientId);
208    
209                String destination = subscriber.getDestinationName();
210                String safeDestinationId = type + asID(destination);
211                destinations.put(safeDestinationId, destination);
212    
213                String selector = subscriber.getSelector();
214    
215                // lets write out the links
216                String subscriberId = safeClientId + "_" + subscriber.getSessionId() + "_" + subscriber.getSubcriptionId();
217    
218                writer.print(subscriberId);
219                writer.print(" -> ");
220                writer.print(safeClientId);
221                writer.println(";");
222    
223                writer.print(safeDestinationId);
224                writer.print(" -> ");
225                writer.print(subscriberId);
226                writer.println(";");
227    
228                // now lets write out the label
229                writer.print(subscriberId);
230                writer.print(" [label = \"");
231                String label = "Subscription: " + subscriber.getSessionId() + "-" + subscriber.getSubcriptionId();
232                if (selector != null && selector.length() > 0) {
233                    label = label + "\\nSelector: " + selector;
234                }
235                writer.print(label);
236                writer.println("\"];");
237            }
238        }
239    
240        protected void writeLabels(PrintWriter writer, String color, String prefix, Map<String, String> map) {
241            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
242                Map.Entry entry = (Map.Entry)iter.next();
243                String id = (String)entry.getKey();
244                String label = (String)entry.getValue();
245    
246                writer.print(id);
247                writer.print(" [ fillcolor = ");
248                writer.print(color);
249                writer.print(", label = \"");
250                writer.print(prefix);
251                writer.print(label);
252                writer.println("\"];");
253            }
254        }
255    
256        /**
257         * Lets strip out any non supported characters
258         */
259        protected String asID(String name) {
260            StringBuffer buffer = new StringBuffer();
261            int size = name.length();
262            for (int i = 0; i < size; i++) {
263                char ch = name.charAt(i);
264                if (Character.isLetterOrDigit(ch) || ch == '_') {
265                    buffer.append(ch);
266                } else {
267                    buffer.append('_');
268                }
269            }
270            return buffer.toString();
271        }
272    
273        protected void printNodes(PrintWriter writer, DestinationMapNode node, String prefix) {
274            String path = getPath(node);
275            writer.print("  ");
276            writer.print(prefix);
277            writer.print(ID_SEPARATOR);
278            writer.print(path);
279            String label = path;
280            if (prefix.equals("topic")) {
281                label = "Topics";
282            } else if (prefix.equals("queue")) {
283                label = "Queues";
284            }
285            writer.print("[ label = \"");
286            writer.print(label);
287            writer.println("\" ];");
288    
289            Collection children = node.getChildren();
290            for (Iterator iter = children.iterator(); iter.hasNext();) {
291                DestinationMapNode child = (DestinationMapNode)iter.next();
292                printNodes(writer, child, prefix + ID_SEPARATOR + path);
293            }
294        }
295    
296        protected void printNodeLinks(PrintWriter writer, DestinationMapNode node, String prefix) {
297            String path = getPath(node);
298            Collection children = node.getChildren();
299            for (Iterator iter = children.iterator(); iter.hasNext();) {
300                DestinationMapNode child = (DestinationMapNode)iter.next();
301    
302                writer.print("  ");
303                writer.print(prefix);
304                writer.print(ID_SEPARATOR);
305                writer.print(path);
306                writer.print(" -> ");
307                writer.print(prefix);
308                writer.print(ID_SEPARATOR);
309                writer.print(path);
310                writer.print(ID_SEPARATOR);
311                writer.print(getPath(child));
312                writer.println(";");
313    
314                printNodeLinks(writer, child, prefix + ID_SEPARATOR + path);
315            }
316        }
317    
318        protected String getPath(DestinationMapNode node) {
319            String path = node.getPath();
320            if (path.equals("*")) {
321                return "root";
322            }
323            return path;
324        }
325        
326        BrokerViewMBean getBrokerView() throws Exception {
327            if (this.brokerView == null) {
328                ObjectName brokerName = getBrokerService().getBrokerObjectName();
329                this.brokerView = (BrokerViewMBean) getBrokerService().getManagementContext().newProxyInstance(brokerName,
330                        BrokerViewMBean.class, true);
331            }
332            return this.brokerView;
333        }
334    }