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 */
017package org.apache.activemq.console.command;
018
019import java.util.Collections;
020import java.util.Comparator;
021import java.util.List;
022import java.util.Locale;
023import javax.management.MBeanServerInvocationHandler;
024import javax.management.ObjectInstance;
025import javax.management.ObjectName;
026
027import org.apache.activemq.broker.jmx.QueueView;
028import org.apache.activemq.broker.jmx.QueueViewMBean;
029import org.apache.activemq.broker.jmx.TopicView;
030import org.apache.activemq.broker.jmx.TopicViewMBean;
031import org.apache.activemq.console.util.JmxMBeansUtil;
032
033public class DstatCommand extends AbstractJmxCommand {
034
035    private static final String queryString =
036        "type=Broker,brokerName=*,destinationType=%1,destinationName=*,*";
037
038    protected String[] helpFile = new String[] {
039        "Task Usage: activemq-admin dstat [dstat-options] [destination-type]",
040        "Description: Performs a predefined query that displays useful statistics regarding the specified .",
041        "             destination type (Queues or Topics) and displays those results in a tabular format.",
042        "             If no broker name is specified, it will try and select from all registered brokers.",
043        "",
044        "dstat Options:",
045        "    --jmxurl <url>                Set the JMX URL to connect to.",
046        "    --pid <pid>                   Set the pid to connect to (only on Sun JVM).",
047        "    --jmxuser <user>              Set the JMX user used for authenticating.",
048        "    --jmxpassword <password>      Set the JMX password used for authenticating.",
049        "    --jmxlocal                    Use the local JMX server instead of a remote one.",
050        "    --version                     Display the version information.",
051        "    -h,-?,--help                  Display the query broker help information.",
052        "",
053        "Examples:",
054        "    activemq-admin dstat queues",
055        "        - Display a tabular summary of statistics for the queues on the broker.",
056        "    activemq-admin dstat topics",
057        "        - Display a tabular summary of statistics for the queues on the broker."
058    };
059
060    /**
061     * Execute the dstat command, which allows you to display information for topics or queue in
062     * a tabular format.
063     *
064     * @param tokens - command arguments
065     * @throws Exception
066     */
067    @Override
068    protected void runTask(List<String> tokens) throws Exception {
069        try {
070
071            if (tokens.contains("topics")) {
072                displayTopicStats();
073            } else if (tokens.contains("queues")) {
074                displayQueueStats();
075            } else {
076                displayAllDestinations();
077            }
078
079            // Iterate through the queue names
080        } catch (Exception e) {
081            context.printException(new RuntimeException("Failed to execute dstat task. Reason: " + e.getMessage(), e));
082            throw new Exception(e);
083        }
084    }
085
086    @SuppressWarnings("unchecked")
087    private void displayAllDestinations() throws Exception {
088
089        String query = JmxMBeansUtil.createQueryString(queryString, "*");
090        List queueList = JmxMBeansUtil.queryMBeans(createJmxConnection(), query);
091
092        final String header = "%-50s  %10s  %10s  %10s  %10s  %10s  %10s  %10s";
093        final String tableRow = "%-50s  %10d  %10d  %10d  %10d  %10d  %10d  %10d";
094
095        // sort list so the names is A..Z
096        Collections.sort(queueList, new ObjectInstanceComparator());
097
098        context.print(String.format(Locale.US, header, "Name", "Queue Size", "Producer #", "Consumer #", "Enqueue #", "Dequeue #", "Forward #", "Memory %"));
099
100        // Iterate through the queue result
101        for (Object view : queueList) {
102            ObjectInstance obj = (ObjectInstance) view;
103            if (!filterMBeans(obj)) {
104                continue;
105            }
106            ObjectName queueName = obj.getObjectName();
107
108            QueueViewMBean queueView = MBeanServerInvocationHandler.
109                newProxyInstance(createJmxConnection(), queueName, QueueViewMBean.class, true);
110
111            context.print(String.format(Locale.US, tableRow,
112                    queueView.getName(),
113                    queueView.getQueueSize(),
114                    queueView.getProducerCount(),
115                    queueView.getConsumerCount(),
116                    queueView.getEnqueueCount(),
117                    queueView.getDequeueCount(),
118                    queueView.getForwardCount(),
119                    queueView.getMemoryPercentUsage()));
120        }
121    }
122
123    @SuppressWarnings("unchecked")
124    private void displayQueueStats() throws Exception {
125
126        String query = JmxMBeansUtil.createQueryString(queryString, "Queue");
127        List queueList = JmxMBeansUtil.queryMBeans(createJmxConnection(), query);
128
129        final String header = "%-50s  %10s  %10s  %10s  %10s  %10s  %10s  %10s  %10s";
130        final String tableRow = "%-50s  %10d  %10d  %10d  %10d  %10d  %10d  %10d  %10d";
131
132        context.print(String.format(Locale.US, header, "Name", "Queue Size", "Producer #", "Consumer #", "Enqueue #", "Dequeue #", "Forward #", "Memory %", "Inflight #"));
133
134        Collections.sort(queueList, new ObjectInstanceComparator());
135
136        // Iterate through the queue result
137        for (Object view : queueList) {
138            ObjectInstance obj = (ObjectInstance) view;
139            if (!filterMBeans(obj)) {
140                continue;
141            }
142            ObjectName queueName = obj.getObjectName();
143
144            QueueViewMBean queueView = MBeanServerInvocationHandler.
145                newProxyInstance(createJmxConnection(), queueName, QueueViewMBean.class, true);
146
147            context.print(String.format(Locale.US, tableRow,
148                    queueView.getName(),
149                    queueView.getQueueSize(),
150                    queueView.getProducerCount(),
151                    queueView.getConsumerCount(),
152                    queueView.getEnqueueCount(),
153                    queueView.getDequeueCount(),
154                    queueView.getForwardCount(),
155                    queueView.getMemoryPercentUsage(),
156                    queueView.getInFlightCount()));
157        }
158    }
159
160    @SuppressWarnings("unchecked")
161    private void displayTopicStats() throws Exception {
162
163        String query = JmxMBeansUtil.createQueryString(queryString, "Topic");
164        List topicsList = JmxMBeansUtil.queryMBeans(createJmxConnection(), query);
165
166        final String header = "%-50s  %10s  %10s  %10s  %10s  %10s  %10s  %10s";
167        final String tableRow = "%-50s  %10d  %10d  %10d  %10d  %10d  %10d  %10d";
168
169        // sort list so the names is A..Z
170        Collections.sort(topicsList, new ObjectInstanceComparator());
171
172        context.print(String.format(Locale.US, header, "Name", "Queue Size", "Producer #", "Consumer #", "Enqueue #", "Dequeue #", "Forward #", "Memory %"));
173
174        // Iterate through the topics result
175        for (Object view : topicsList) {
176            ObjectInstance obj = (ObjectInstance) view;
177            if (!filterMBeans(obj)) {
178                continue;
179            }
180            ObjectName topicName = obj.getObjectName();
181
182            TopicViewMBean topicView = MBeanServerInvocationHandler.
183                newProxyInstance(createJmxConnection(), topicName, TopicViewMBean.class, true);
184
185            context.print(String.format(Locale.US, tableRow,
186                    topicView.getName(),
187                    topicView.getQueueSize(),
188                    topicView.getProducerCount(),
189                    topicView.getConsumerCount(),
190                    topicView.getEnqueueCount(),
191                    topicView.getDequeueCount(),
192                    topicView.getForwardCount(),
193                    topicView.getMemoryPercentUsage()));
194        }
195    }
196
197    @Override
198    public String getName() {
199        return "dstat";
200    }
201
202    @Override
203    public String getOneLineDescription() {
204        return "Performs a predefined query that displays useful tabular statistics regarding the specified destination type";
205    }
206
207    /**
208     * Print the help messages for this command
209     */
210    @Override
211    protected void printHelp() {
212        context.printHelp(helpFile);
213    }
214
215    protected boolean filterMBeans(ObjectInstance obj) {
216        String className = obj.getClassName();
217        return className.equals(QueueView.class.getName()) || className.equals(TopicView.class.getName());
218    }
219
220    private static class ObjectInstanceComparator implements Comparator<ObjectInstance> {
221
222        @Override
223        public int compare(ObjectInstance o1, ObjectInstance o2) {
224            return o1.getObjectName().compareTo(o2.getObjectName());
225        }
226    }
227
228}