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