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.ArrayList; 020import java.util.HashSet; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Set; 024import java.util.StringTokenizer; 025 026import javax.management.ObjectInstance; 027 028import org.apache.activemq.console.util.AmqMessagesUtil; 029import org.apache.activemq.console.util.JmxMBeansUtil; 030 031public class BrowseCommand extends AbstractJmxCommand { 032 033 public static final String QUEUE_PREFIX = "queue:"; 034 public static final String TOPIC_PREFIX = "topic:"; 035 036 public static final String VIEW_GROUP_HEADER = "header:"; 037 public static final String VIEW_GROUP_CUSTOM = "custom:"; 038 public static final String VIEW_GROUP_BODY = "body:"; 039 040 protected String[] helpFile = new String[] { 041 "Task Usage: Main browse [browse-options] <destinations>", "Description: Display selected destination's messages.", 042 "", 043 "Browse Options:", 044 " --msgsel <msgsel1,msglsel2> Add to the search list messages matched by the query similar to", 045 " the messages selector format.", 046 " -V<header|custom|body> Predefined view that allows you to view the message header, custom", 047 " message header, or the message body.", 048 " --view <attr1>,<attr2>,... Select the specific attribute of the message to view.", 049 " --jmxurl <url> Set the JMX URL to connect to.", 050 " --pid <pid> Set the pid to connect to (only on Sun JVM).", 051 " --jmxuser <user> Set the JMX user used for authenticating.", 052 " --jmxpassword <password> Set the JMX password used for authenticating.", 053 " --jmxlocal Use the local JMX server instead of a remote one.", 054 " --version Display the version information.", 055 " -h,-?,--help Display the browse broker help information.", 056 "", 057 "Examples:", 058 " Main browse FOO.BAR", 059 " - Print the message header, custom message header, and message body of all messages in the", 060 " queue FOO.BAR", 061 "", 062 " Main browse -Vheader,body queue:FOO.BAR", 063 " - Print only the message header and message body of all messages in the queue FOO.BAR", 064 "", 065 " Main browse -Vheader --view custom:MyField queue:FOO.BAR", 066 " - Print the message header and the custom field 'MyField' of all messages in the queue FOO.BAR", 067 "", 068 " Main browse --msgsel \"JMSMessageID='*:10',JMSPriority>5\" FOO.BAR", 069 " - Print all the message fields that has a JMSMessageID in the header field that matches the", 070 " wildcard *:10, and has a JMSPriority field > 5 in the queue FOO.BAR.", 071 " SLQ92 syntax is also supported.", 072 " * To use wildcard queries, the field must be a string and the query enclosed in ''", 073 " Use double quotes \"\" around the entire message selector string.", 074 "" 075 }; 076 077 private final List<String> queryAddObjects = new ArrayList<String>(10); 078 private final List<String> querySubObjects = new ArrayList<String>(10); 079 private final Set<String> groupViews = new HashSet<String>(10); 080 private final Set queryViews = new HashSet(10); 081 082 @Override 083 public String getName() { 084 return "browse"; 085 } 086 087 @Override 088 public String getOneLineDescription() { 089 return "Used to browse a destination"; 090 } 091 092 /** 093 * Execute the browse command, which allows you to browse the messages in a 094 * given JMS destination 095 * 096 * @param tokens - command arguments 097 * @throws Exception 098 */ 099 protected void runTask(List<String> tokens) throws Exception { 100 try { 101 // If there is no queue name specified, let's select all 102 if (tokens.isEmpty()) { 103 tokens.add("*"); 104 } 105 106 // Iterate through the queue names 107 for (Iterator<String> i = tokens.iterator(); i.hasNext();) { 108 List queueList = JmxMBeansUtil.queryMBeans(createJmxConnection(), "Type=Queue,Destination=" + i.next() + ",*"); 109 110 // Iterate through the queue result 111 for (Iterator j = queueList.iterator(); j.hasNext();) { 112 List messages = JmxMBeansUtil.createMessageQueryFilter(createJmxConnection(), ((ObjectInstance)j.next()).getObjectName()).query(queryAddObjects); 113 context.printMessage(JmxMBeansUtil.filterMessagesView(messages, groupViews, queryViews)); 114 } 115 } 116 } catch (Exception e) { 117 context.printException(new RuntimeException("Failed to execute browse task. Reason: " + e)); 118 throw new Exception(e); 119 } 120 } 121 122 /** 123 * Handle the --msgsel, --xmsgsel, --view, -V options. 124 * 125 * @param token - option token to handle 126 * @param tokens - succeeding command arguments 127 * @throws Exception 128 */ 129 protected void handleOption(String token, List<String> tokens) throws Exception { 130 131 // If token is an additive message selector option 132 if (token.startsWith("--msgsel")) { 133 134 // If no message selector is specified, or next token is a new 135 // option 136 if (tokens.isEmpty() || ((String)tokens.get(0)).startsWith("-")) { 137 context.printException(new IllegalArgumentException("Message selector not specified")); 138 return; 139 } 140 141 StringTokenizer queryTokens = new StringTokenizer((String)tokens.remove(0), COMMAND_OPTION_DELIMETER); 142 while (queryTokens.hasMoreTokens()) { 143 queryAddObjects.add(queryTokens.nextToken()); 144 } 145 } else if (token.startsWith("--xmsgsel")) { 146 // If token is a substractive message selector option 147 148 // If no message selector is specified, or next token is a new 149 // option 150 if (tokens.isEmpty() || ((String)tokens.get(0)).startsWith("-")) { 151 context.printException(new IllegalArgumentException("Message selector not specified")); 152 return; 153 } 154 155 StringTokenizer queryTokens = new StringTokenizer((String)tokens.remove(0), COMMAND_OPTION_DELIMETER); 156 while (queryTokens.hasMoreTokens()) { 157 querySubObjects.add(queryTokens.nextToken()); 158 } 159 160 } else if (token.startsWith("--view")) { 161 // If token is a view option 162 163 // If no view specified, or next token is a new option 164 if (tokens.isEmpty() || ((String)tokens.get(0)).startsWith("-")) { 165 context.printException(new IllegalArgumentException("Attributes to view not specified")); 166 return; 167 } 168 169 // Add the attributes to view 170 StringTokenizer viewTokens = new StringTokenizer((String)tokens.remove(0), COMMAND_OPTION_DELIMETER); 171 while (viewTokens.hasMoreTokens()) { 172 String viewToken = viewTokens.nextToken(); 173 174 // If view is explicitly specified to belong to the JMS header 175 if (viewToken.equals(VIEW_GROUP_HEADER)) { 176 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + viewToken.substring(VIEW_GROUP_HEADER.length())); 177 178 // If view is explicitly specified to belong to the JMS 179 // custom header 180 } else if (viewToken.equals(VIEW_GROUP_CUSTOM)) { 181 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_CUSTOM_PREFIX + viewToken.substring(VIEW_GROUP_CUSTOM.length())); 182 183 // If view is explicitly specified to belong to the JMS body 184 } else if (viewToken.equals(VIEW_GROUP_BODY)) { 185 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + viewToken.substring(VIEW_GROUP_BODY.length())); 186 187 // If no view explicitly specified, let's check the view for 188 // each group 189 } else { 190 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + viewToken); 191 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_CUSTOM_PREFIX + viewToken); 192 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + viewToken); 193 } 194 } 195 } else if (token.startsWith("-V")) { 196 // If token is a predefined group view option 197 String viewGroup = token.substring(2); 198 // If option is a header group view 199 if (viewGroup.equals("header")) { 200 groupViews.add(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX); 201 202 // If option is a custom header group view 203 } else if (viewGroup.equals("custom")) { 204 groupViews.add(AmqMessagesUtil.JMS_MESSAGE_CUSTOM_PREFIX); 205 206 // If option is a body group view 207 } else if (viewGroup.equals("body")) { 208 groupViews.add(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX); 209 210 // Unknown group view 211 } else { 212 context.printInfo("Unknown group view: " + viewGroup + ". Ignoring group view option."); 213 } 214 } else { 215 // Let super class handle unknown option 216 super.handleOption(token, tokens); 217 } 218 } 219 220 /** 221 * Print the help messages for the browse command 222 */ 223 protected void printHelp() { 224 context.printHelp(helpFile); 225 } 226 227}