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 javax.management.ObjectName; 020import org.apache.activemq.console.util.JmxMBeansUtil; 021 022import java.util.*; 023 024public class QueryCommand extends AbstractJmxCommand { 025 // Predefined type=identifier query 026 private static final Properties PREDEFINED_OBJNAME_QUERY = new Properties(); 027 028 static { 029 PREDEFINED_OBJNAME_QUERY.setProperty("Broker", "brokerName=%1"); 030 PREDEFINED_OBJNAME_QUERY.setProperty("Connection", "connector=clientConnectors,connectionViewType=*,connectionName=%1,*"); 031 PREDEFINED_OBJNAME_QUERY.setProperty("Connector", "connector=clientConnectors,connectorName=%1"); 032 PREDEFINED_OBJNAME_QUERY.setProperty("NetworkConnector", "connector=networkConnectors,networkConnectorName=%1"); 033 PREDEFINED_OBJNAME_QUERY.setProperty("Queue", "destinationType=Queue,destinationName=%1"); 034 PREDEFINED_OBJNAME_QUERY.setProperty("Topic", "destinationType=Topic,destinationName=%1"); 035 }; 036 037 protected String[] helpFile = new String[] { 038 "Task Usage: Main query [query-options]", 039 "Description: Display selected broker component's attributes and statistics.", 040 "", 041 "Query Options:", 042 " -Q<type>=<name> Add to the search list the specific object type matched", 043 " by the defined object identifier.", 044 " -xQ<type>=<name> Remove from the search list the specific object type", 045 " matched by the object identifier.", 046 " --objname <query> Add to the search list objects matched by the query similar", 047 " to the JMX object name format.", 048 " --xobjname <query> Remove from the search list objects matched by the query", 049 " similar to the JMX object name format.", 050 " --view <attr1>,<attr2>,... Select the specific attribute of the object to view.", 051 " By default all attributes will be displayed.", 052 " --invoke <operation> Specify the operation to invoke on matching objects", 053 " --jmxurl <url> Set the JMX URL to connect to.", 054 " --pid <pid> Set the pid to connect to (only on Sun JVM).", 055 " --jmxuser <user> Set the JMX user used for authenticating.", 056 " --jmxpassword <password> Set the JMX password used for authenticating.", 057 " --jmxlocal Use the local JMX server instead of a remote one.", 058 " --version Display the version information.", 059 " -h,-?,--help Display the query broker help information.", 060 "", "Examples:", 061 " query", 062 " - Print all the attributes of all registered objects queues, topics, connections, etc).", 063 "", 064 " query -QQueue=TEST.FOO", 065 " - Print all the attributes of the queue with destination name TEST.FOO.", 066 "", 067 " query -QTopic=*", 068 " - Print all the attributes of all registered topics.", 069 "", 070 " query --view EnqueueCount,DequeueCount", 071 " - Print the attributes EnqueueCount and DequeueCount of all registered objects.", 072 "", 073 " query -QTopic=* --view EnqueueCount,DequeueCount", 074 " - Print the attributes EnqueueCount and DequeueCount of all registered topics.", 075 "", 076 " query -QTopic=* -QQueue=* --view EnqueueCount,DequeueCount", 077 " - Print the attributes EnqueueCount and DequeueCount of all registered topics and", 078 " queues.", 079 "", 080 " query -QTopic=* -xQTopic=ActiveMQ.Advisory.*", 081 " - Print all attributes of all topics except those that has a name that begins", 082 " with \"ActiveMQ.Advisory\".", 083 "", 084 " query --objname type=Broker,brokerName=*,connector=clientConnectors,connectorName=* -xQNetworkConnector=*", 085 " - Print all attributes of all connectors, connections excluding network connectors", 086 " that belongs to the broker that begins with local.", 087 "", 088 " query -QQueue=* -xQQueue=????", 089 " - Print all attributes of all queues except those that are 4 letters long.", 090 "", 091 " query -QQueue=* --invoke pause", 092 " - Pause all queues.", 093 "", 094 095 }; 096 097 private final List<String> queryAddObjects = new ArrayList<String>(10); 098 private final List<String> querySubObjects = new ArrayList<String>(10); 099 private final Set queryViews = new LinkedHashSet(); 100 private final List<String> opAndParams = new ArrayList<String>(10); 101 102 @Override 103 public String getName() { 104 return "query"; 105 } 106 107 @Override 108 public String getOneLineDescription() { 109 return "Display selected broker component's attributes and statistics."; 110 } 111 112 /** 113 * Queries the mbeans registered in the specified JMX context 114 * 115 * @param tokens - command arguments 116 * @throws Exception 117 */ 118 protected void runTask(List<String> tokens) throws Exception { 119 try { 120 // Query for the mbeans to add 121 Map<Object,List> addMBeans = JmxMBeansUtil.queryMBeansAsMap(createJmxConnection(), queryAddObjects, queryViews); 122 // Query for the mbeans to sub 123 if (querySubObjects.size() > 0) { 124 Map<Object,List> subMBeans = JmxMBeansUtil.queryMBeansAsMap(createJmxConnection(), querySubObjects, queryViews); 125 addMBeans.keySet().removeAll(subMBeans.keySet()); 126 } 127 128 if (opAndParams.isEmpty()) { 129 context.printMBean(JmxMBeansUtil.filterMBeansView(new ArrayList(addMBeans.values()), queryViews)); 130 } else { 131 context.print(doInvoke(addMBeans.keySet(), opAndParams)); 132 } 133 } catch (Exception e) { 134 context.printException(new RuntimeException("Failed to execute query task. Reason: " + e)); 135 throw new Exception(e); 136 } 137 } 138 139 private Collection doInvoke(Set<Object> mBeans, List<String> opAndParams) throws Exception { 140 LinkedList<String> results = new LinkedList<>(); 141 for (Object objectName : mBeans) { 142 Object result = createJmxConnection().invoke((ObjectName) objectName, opAndParams.get(0), 143 params(opAndParams), stringSignature(opAndParams)); 144 results.add("[" + objectName + "]." + opAndParams.get(0) + " = " + result); 145 } 146 return results; 147 } 148 149 private Object[] params(List<String> opAndParams) { 150 if (opAndParams.size() > 1) { 151 return opAndParams.subList(1, opAndParams.size()).toArray(); 152 } else { 153 return null; 154 } 155 } 156 157 private String[] stringSignature(List<String> opAndParams) { 158 if (opAndParams.size() > 1) { 159 String[] sig = new String[opAndParams.size() - 1]; 160 Arrays.fill(sig, String.class.getName()); 161 return sig; 162 } else { 163 return null; 164 } 165 } 166 167 168 /** 169 * Handle the -Q, -xQ, --objname, --xobjname, --view --invoke options. 170 * 171 * @param token - option token to handle 172 * @param tokens - succeeding command arguments 173 * @throws Exception 174 */ 175 protected void handleOption(String token, List<String> tokens) throws Exception { 176 // If token is a additive predefined query define option 177 if (token.startsWith("-Q")) { 178 String key = token.substring(2); 179 String value = ""; 180 int pos = key.indexOf("="); 181 if (pos >= 0) { 182 value = key.substring(pos + 1); 183 key = key.substring(0, pos); 184 } 185 186 // If additive query 187 String predefQuery = PREDEFINED_OBJNAME_QUERY.getProperty(key); 188 if (predefQuery == null) { 189 context.printException(new IllegalArgumentException("Unknown query object type: " + key)); 190 return; 191 } 192 String queryStr = JmxMBeansUtil.createQueryString(predefQuery, value); 193 StringTokenizer queryTokens = new StringTokenizer(queryStr, COMMAND_OPTION_DELIMETER); 194 while (queryTokens.hasMoreTokens()) { 195 queryAddObjects.add(queryTokens.nextToken()); 196 } 197 normaliseObjectName(queryAddObjects); 198 } else if (token.startsWith("-xQ")) { 199 // If token is a substractive predefined query define option 200 String key = token.substring(3); 201 String value = ""; 202 int pos = key.indexOf("="); 203 if (pos >= 0) { 204 value = key.substring(pos + 1); 205 key = key.substring(0, pos); 206 } 207 208 // If subtractive query 209 String predefQuery = PREDEFINED_OBJNAME_QUERY.getProperty(key); 210 if (predefQuery == null) { 211 context.printException(new IllegalArgumentException("Unknown query object type: " + key)); 212 return; 213 } 214 String queryStr = JmxMBeansUtil.createQueryString(predefQuery, value); 215 StringTokenizer queryTokens = new StringTokenizer(queryStr, COMMAND_OPTION_DELIMETER); 216 while (queryTokens.hasMoreTokens()) { 217 querySubObjects.add(queryTokens.nextToken()); 218 } 219 normaliseObjectName(querySubObjects); 220 } else if (token.startsWith("--objname")) { 221 // If token is an additive object name query option 222 223 // If no object name query is specified, or next token is a new 224 // option 225 if (tokens.isEmpty() || ((String)tokens.get(0)).startsWith("-")) { 226 context.printException(new IllegalArgumentException("Object name query not specified")); 227 return; 228 } 229 230 StringTokenizer queryTokens = new StringTokenizer((String)tokens.remove(0), COMMAND_OPTION_DELIMETER); 231 while (queryTokens.hasMoreTokens()) { 232 queryAddObjects.add(queryTokens.nextToken()); 233 } 234 } else if (token.startsWith("--xobjname")) { 235 // If token is a substractive object name query option 236 237 // If no object name query is specified, or next token is a new 238 // option 239 if (tokens.isEmpty() || ((String)tokens.get(0)).startsWith("-")) { 240 context.printException(new IllegalArgumentException("Object name query not specified")); 241 return; 242 } 243 244 StringTokenizer queryTokens = new StringTokenizer((String)tokens.remove(0), COMMAND_OPTION_DELIMETER); 245 while (queryTokens.hasMoreTokens()) { 246 querySubObjects.add(queryTokens.nextToken()); 247 } 248 } else if (token.startsWith("--view")) { 249 // If token is a view option 250 251 // If no view specified, or next token is a new option 252 if (tokens.isEmpty() || ((String)tokens.get(0)).startsWith("-")) { 253 context.printException(new IllegalArgumentException("Attributes to view not specified")); 254 return; 255 } 256 257 // Add the attributes to view 258 Enumeration viewTokens = new StringTokenizer((String)tokens.remove(0), COMMAND_OPTION_DELIMETER); 259 while (viewTokens.hasMoreElements()) { 260 queryViews.add(viewTokens.nextElement()); 261 } 262 } else if (token.startsWith("--invoke")) { 263 264 if (tokens.isEmpty() || ((String)tokens.get(0)).startsWith("-")) { 265 context.printException(new IllegalArgumentException("operation to invoke is not specified")); 266 return; 267 } 268 269 // add op and params 270 Enumeration viewTokens = new StringTokenizer((String)tokens.remove(0), COMMAND_OPTION_DELIMETER); 271 while (viewTokens.hasMoreElements()) { 272 opAndParams.add((String)viewTokens.nextElement()); 273 } 274 275 } else { 276 // Let super class handle unknown option 277 super.handleOption(token, tokens); 278 } 279 } 280 281 private void normaliseObjectName(List<String> queryAddObjects) { 282 ensurePresent(queryAddObjects, "type", "Broker"); 283 ensurePresent(queryAddObjects, "brokerName", "*"); 284 285 // -QQueue && -QTopic 286 ensureUnique(queryAddObjects, "destinationType", "?????"); 287 ensureUnique(queryAddObjects, "destinationName", "*"); 288 } 289 290 private void ensurePresent(List<String> queryAddObjects, String id, String wildcard) { 291 List<String> matches = findMatchingKeys(queryAddObjects, id); 292 if (matches.size() == 0) { 293 queryAddObjects.add(id + "=" + wildcard); 294 } 295 } 296 297 private void ensureUnique(List<String> queryAddObjects, String id, String wildcard) { 298 List<String> matches = findMatchingKeys(queryAddObjects, id); 299 if (matches.size() > 1) { 300 queryAddObjects.removeAll(matches); 301 queryAddObjects.add(id + "=" + wildcard); 302 } 303 } 304 305 private List<String> findMatchingKeys(List<String> queryAddObjects, String id) { 306 List<String> matches = new LinkedList<>(); 307 for (String prop : queryAddObjects) { 308 String[] keyValue = prop.split("="); 309 if (keyValue.length == 2 && keyValue[0].equals(id)) { 310 matches.add(prop); 311 } 312 } 313 return matches; 314 } 315 316 /** 317 * Print the help messages for the browse command 318 */ 319 protected void printHelp() { 320 context.printHelp(helpFile); 321 } 322 323}