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.jms.Destination; 027 028import org.apache.activemq.command.ActiveMQQueue; 029import org.apache.activemq.command.ActiveMQTopic; 030import org.apache.activemq.console.util.AmqMessagesUtil; 031 032public class AmqBrowseCommand extends AbstractAmqCommand { 033 034 public static final String QUEUE_PREFIX = "queue:"; 035 public static final String TOPIC_PREFIX = "topic:"; 036 037 public static final String VIEW_GROUP_HEADER = "header:"; 038 public static final String VIEW_GROUP_CUSTOM = "custom:"; 039 public static final String VIEW_GROUP_BODY = "body:"; 040 041 protected String[] helpFile = new String[] { 042 "Task Usage: Main browse --amqurl <broker url> [browse-options] <destinations>", 043 "Description: Display selected destination's messages.", 044 "", 045 "Browse Options:", 046 " --amqurl <url> Set the broker URL to connect to.", 047 " --msgsel <msgsel1,msglsel2> Add to the search list messages matched by the query similar to", 048 " the messages selector format.", 049 " --factory <className> Load className as the javax.jms.ConnectionFactory to use for creating connections.", 050 " --passwordFactory <className> Load className as the org.apache.activemq.console.command.PasswordFactory", 051 " for retrieving the password from a keystore.", 052 " --user <username> Username to use for JMS connections.", 053 " --password <password> Password to use for JMS connections.", 054 " -V<header|custom|body> Predefined view that allows you to view the message header, custom", 055 " message header, or the message body.", 056 " --view <attr1>,<attr2>,... Select the specific attribute of the message to view.", 057 " --version Display the version information.", 058 " -h,-?,--help Display the browse broker help information.", 059 "", 060 "Examples:", 061 " Main browse --amqurl tcp://localhost:61616 FOO.BAR", 062 " - Print the message header, custom message header, and message body of all messages in the", 063 " queue FOO.BAR", 064 "", 065 " Main browse --amqurl tcp://localhost:61616 -Vheader,body queue:FOO.BAR", 066 " - Print only the message header and message body of all messages in the queue FOO.BAR", 067 "", 068 " Main browse --amqurl tcp://localhost:61616 -Vheader --view custom:MyField queue:FOO.BAR", 069 " - Print the message header and the custom field 'MyField' of all messages in the queue FOO.BAR", 070 "", 071 " Main browse --amqurl tcp://localhost:61616 --msgsel JMSMessageID='*:10',JMSPriority>5 FOO.BAR", 072 " - Print all the message fields that has a JMSMessageID in the header field that matches the", 073 " wildcard *:10, and has a JMSPriority field > 5 in the queue FOO.BAR", 074 " * To use wildcard queries, the field must be a string and the query enclosed in ''", 075 "", 076 " Main browse --amqurl tcp://localhost:61616 --user someUser --password somePass FOO.BAR", 077 " - Print the message header, custom message header, and message body of all messages in the", 078 " queue FOO.BAR, using someUser as the user name, and somePass as the password", 079 "", 080 " Main browse --amqurl tcp://localhost:61616 --user someUser --password somePass --factory org.apache.activemq.ActiveMQConnectionFactory --passwordFactory org.apache.activemq.AMQPasswordFactory FOO.BAR", 081 " - Print the message header, custom message header, and message body of all messages in the", 082 " queue FOO.BAR, using someUser as the user name, org.apache.activemq.AMQFactorySubClass to create JMS connections,", 083 " and org.apache.activemq.console.command.DefaultPasswordFactory to turn somePass into the password to be used.", 084 "", 085 }; 086 087 private final List<String> queryAddObjects = new ArrayList<String>(10); 088 private final List<String> querySubObjects = new ArrayList<String>(10); 089 private final Set<String> groupViews = new HashSet<String>(10); 090 private final Set queryViews = new HashSet(10); 091 092 @Override 093 public String getName() { 094 return "browse"; 095 } 096 097 @Override 098 public String getOneLineDescription() { 099 return "Display selected messages in a specified destination."; 100 } 101 102 /** 103 * Execute the browse command, which allows you to browse the messages in a 104 * given JMS destination 105 * 106 * @param tokens - command arguments 107 * @throws Exception 108 */ 109 protected void runTask(List tokens) throws Exception { 110 try { 111 // If no destination specified 112 if (tokens.isEmpty()) { 113 context.printException(new IllegalArgumentException("No JMS destination specified.")); 114 return; 115 } 116 117 // If no broker url specified 118 if (getBrokerUrl() == null) { 119 context.printException(new IllegalStateException("No broker url specified. Use the --amqurl option to specify a broker url.")); 120 return; 121 } 122 123 // Display the messages for each destination 124 for (Iterator i = tokens.iterator(); i.hasNext();) { 125 String destName = (String)i.next(); 126 Destination dest; 127 128 // If destination has been explicitly specified as a queue 129 if (destName.startsWith(QUEUE_PREFIX)) { 130 dest = new ActiveMQQueue(destName.substring(QUEUE_PREFIX.length())); 131 132 // If destination has been explicitly specified as a topic 133 } else if (destName.startsWith(TOPIC_PREFIX)) { 134 dest = new ActiveMQTopic(destName.substring(TOPIC_PREFIX.length())); 135 136 // By default destination is assumed to be a queue 137 } else { 138 dest = new ActiveMQQueue(destName); 139 } 140 141 // Query for the messages to view 142 List addMsgs = AmqMessagesUtil.getMessages(getConnectionFactory(), dest, queryAddObjects); 143 144 // Query for the messages to remove from view 145 if (querySubObjects.size() > 0) { 146 List subMsgs = AmqMessagesUtil.getMessages(getConnectionFactory(), dest, querySubObjects); 147 addMsgs.removeAll(subMsgs); 148 } 149 150 // Display the messages 151 context.printMessage(AmqMessagesUtil.filterMessagesView(addMsgs, groupViews, queryViews)); 152 } 153 154 } catch (Exception e) { 155 context.printException(new RuntimeException("Failed to execute browse task. Reason: " + e)); 156 throw new Exception(e); 157 } 158 } 159 160 /** 161 * Handle the --msgsel, --xmsgsel, --view, -V options. 162 * 163 * @param token - option token to handle 164 * @param tokens - succeeding command arguments 165 * @throws Exception 166 */ 167 protected void handleOption(String token, List tokens) throws Exception { 168 169 // If token is an additive message selector option 170 if (token.startsWith("--msgsel")) { 171 172 // If no message selector is specified, or next token is a new 173 // option 174 if (tokens.isEmpty() || ((String)tokens.get(0)).startsWith("-")) { 175 context.printException(new IllegalArgumentException("Message selector not specified")); 176 return; 177 } 178 179 StringTokenizer queryTokens = new StringTokenizer((String)tokens.remove(0), COMMAND_OPTION_DELIMETER); 180 while (queryTokens.hasMoreTokens()) { 181 queryAddObjects.add(queryTokens.nextToken()); 182 } 183 } else if (token.startsWith("--xmsgsel")) { 184 // If token is a substractive message selector option 185 186 // If no message selector is specified, or next token is a new option 187 if (tokens.isEmpty() || ((String)tokens.get(0)).startsWith("-")) { 188 context.printException(new IllegalArgumentException("Message selector not specified")); 189 return; 190 } 191 192 StringTokenizer queryTokens = new StringTokenizer((String)tokens.remove(0), COMMAND_OPTION_DELIMETER); 193 while (queryTokens.hasMoreTokens()) { 194 querySubObjects.add(queryTokens.nextToken()); 195 } 196 197 } else if (token.startsWith("--view")) { 198 // If token is a view option 199 200 // If no view specified, or next token is a new option 201 if (tokens.isEmpty() || ((String)tokens.get(0)).startsWith("-")) { 202 context.printException(new IllegalArgumentException("Attributes to view not specified")); 203 return; 204 } 205 206 // Add the attributes to view 207 StringTokenizer viewTokens = new StringTokenizer((String)tokens.remove(0), COMMAND_OPTION_DELIMETER); 208 while (viewTokens.hasMoreTokens()) { 209 String viewToken = viewTokens.nextToken(); 210 211 // If view is explicitly specified to belong to the JMS header 212 if (viewToken.equals(VIEW_GROUP_HEADER)) { 213 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + viewToken.substring(VIEW_GROUP_HEADER.length())); 214 215 // If view is explicitly specified to belong to the JMS 216 // custom header 217 } else if (viewToken.equals(VIEW_GROUP_CUSTOM)) { 218 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_CUSTOM_PREFIX + viewToken.substring(VIEW_GROUP_CUSTOM.length())); 219 220 // If view is explicitly specified to belong to the JMS body 221 } else if (viewToken.equals(VIEW_GROUP_BODY)) { 222 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + viewToken.substring(VIEW_GROUP_BODY.length())); 223 224 // If no view explicitly specified, let's check the view for 225 // each group 226 } else { 227 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + viewToken); 228 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_CUSTOM_PREFIX + viewToken); 229 queryViews.add(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + viewToken); 230 } 231 } 232 } else if (token.startsWith("-V")) { 233 // If token is a predefined group view option 234 String viewGroup = token.substring(2); 235 // If option is a header group view 236 if (viewGroup.equals("header")) { 237 groupViews.add(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX); 238 239 // If option is a custom header group view 240 } else if (viewGroup.equals("custom")) { 241 groupViews.add(AmqMessagesUtil.JMS_MESSAGE_CUSTOM_PREFIX); 242 243 // If option is a body group view 244 } else if (viewGroup.equals("body")) { 245 groupViews.add(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX); 246 247 // Unknown group view 248 } else { 249 context.printInfo("Unknown group view: " + viewGroup + ". Ignoring group view option."); 250 } 251 } else { 252 // Let super class handle unknown option 253 super.handleOption(token, tokens); 254 } 255 } 256 257 /** 258 * Print the help messages for the browse command 259 */ 260 protected void printHelp() { 261 context.printHelp(helpFile); 262 } 263 264}