001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 *
020 */
021 package org.apache.qpid.tools.messagestore.commands;
022
023 import org.apache.commons.codec.binary.Hex;
024 import org.apache.mina.common.ByteBuffer;
025 import org.apache.qpid.framing.abstraction.ContentChunk;
026 import org.apache.qpid.server.queue.AMQMessage;
027 import org.apache.qpid.server.queue.QueueEntryImpl;
028 import org.apache.qpid.server.queue.QueueEntry;
029 import org.apache.qpid.tools.messagestore.MessageStoreTool;
030 import org.apache.qpid.tools.utils.Console;
031
032 import java.io.UnsupportedEncodingException;
033 import java.util.Iterator;
034 import java.util.LinkedList;
035 import java.util.List;
036
037 public class Dump extends Show
038 {
039 private static final int LINE_SIZE = 8;
040 private static final String DEFAULT_ENCODING = "utf-8";
041 private static final boolean SPACE_BYTES = true;
042 private static final String BYTE_SPACER = " ";
043 private static final String NON_PRINTING_ASCII_CHAR = "?";
044
045 protected boolean _content = true;
046
047 public Dump(MessageStoreTool tool)
048 {
049 super(tool);
050 }
051
052 public String help()
053 {
054 return "Dump selected message content. Default: show=content";
055 }
056
057 public String usage()
058 {
059 return getCommand() + " [show=[all],[msgheaders],[_amqHeaders],[routing],[content]] [id=<msgid e.g. 1,2,4-10>]";
060 }
061
062 public String getCommand()
063 {
064 return "dump";
065 }
066
067 public void execute(String... args)
068 {
069 assert args.length > 0;
070 assert args[0].equals(getCommand());
071
072
073 if (args.length >= 2)
074 {
075 for (String arg : args)
076 {
077 if (arg.startsWith("show="))
078 {
079 _content = arg.contains("content") || arg.contains("all");
080 }
081 }
082
083 parseArgs(args);
084 }
085
086 performShow();
087 }
088
089
090 protected List<List> createMessageData(java.util.List<Long> msgids, List<QueueEntry> messages, boolean showHeaders, boolean showRouting,
091 boolean showMessageHeaders)
092 {
093
094 List<List> display = new LinkedList<List>();
095
096 List<String> hex = new LinkedList<String>();
097 List<String> ascii = new LinkedList<String>();
098 display.add(hex);
099 display.add(ascii);
100
101 for (QueueEntry entry : messages)
102 {
103 AMQMessage msg = entry.getMessage();
104 if (!includeMsg(msg, msgids))
105 {
106 continue;
107 }
108
109 //Add divider between messages
110 hex.add(Console.ROW_DIVIDER);
111 ascii.add(Console.ROW_DIVIDER);
112
113 // Show general message information
114 hex.add(Show.Columns.ID.name());
115 ascii.add(msg.getMessageId().toString());
116
117 hex.add(Console.ROW_DIVIDER);
118 ascii.add(Console.ROW_DIVIDER);
119
120 if (showRouting)
121 {
122 addShowInformation(hex, ascii, msg, "Routing Details", true, false, false);
123 }
124 if (showHeaders)
125 {
126 addShowInformation(hex, ascii, msg, "Headers", false, true, false);
127 }
128 if (showMessageHeaders)
129 {
130 addShowInformation(hex, ascii, msg, null, false, false, true);
131 }
132
133 // Add Content Body seciont
134 hex.add("Content Body");
135 ascii.add("");
136 hex.add(Console.ROW_DIVIDER);
137 ascii.add(Console.ROW_DIVIDER);
138
139 Iterator bodies = msg.getContentBodyIterator();
140 if (bodies.hasNext())
141 {
142
143 hex.add("Hex");
144 hex.add(Console.ROW_DIVIDER);
145
146
147 ascii.add("ASCII");
148 ascii.add(Console.ROW_DIVIDER);
149
150 while (bodies.hasNext())
151 {
152 ContentChunk chunk = (ContentChunk) bodies.next();
153
154 //Duplicate so we don't destroy original data :)
155 ByteBuffer hexBuffer = chunk.getData().duplicate();
156
157 ByteBuffer charBuffer = hexBuffer.duplicate();
158
159 Hex hexencoder = new Hex();
160
161 while (hexBuffer.hasRemaining())
162 {
163 byte[] line = new byte[LINE_SIZE];
164
165 int bufsize = hexBuffer.remaining();
166 if (bufsize < LINE_SIZE)
167 {
168 hexBuffer.get(line, 0, bufsize);
169 }
170 else
171 {
172 bufsize = line.length;
173 hexBuffer.get(line);
174 }
175
176 byte[] encoded = hexencoder.encode(line);
177
178 try
179 {
180 String encStr = new String(encoded, 0, bufsize * 2, DEFAULT_ENCODING);
181 String hexLine = "";
182
183 int strKength = encStr.length();
184 for (int c = 0; c < strKength; c++)
185 {
186 hexLine += encStr.charAt(c);
187
188 if (c % 2 == 1 && SPACE_BYTES)
189 {
190 hexLine += BYTE_SPACER;
191 }
192 }
193
194 hex.add(hexLine);
195 }
196 catch (UnsupportedEncodingException e)
197 {
198 _console.println(e.getMessage());
199 return null;
200 }
201 }
202
203 while (charBuffer.hasRemaining())
204 {
205 String asciiLine = "";
206
207 for (int pos = 0; pos < LINE_SIZE; pos++)
208 {
209 if (charBuffer.hasRemaining())
210 {
211 byte ch = charBuffer.get();
212
213 if (isPrintable(ch))
214 {
215 asciiLine += (char) ch;
216 }
217 else
218 {
219 asciiLine += NON_PRINTING_ASCII_CHAR;
220 }
221
222 if (SPACE_BYTES)
223 {
224 asciiLine += BYTE_SPACER;
225 }
226 }
227 else
228 {
229 break;
230 }
231 }
232
233 ascii.add(asciiLine);
234 }
235 }
236 }
237 else
238 {
239 List<String> result = new LinkedList<String>();
240
241 display.add(result);
242 result.add("No ContentBodies");
243 }
244 }
245
246 // if hex is empty then we have no data to display
247 if (hex.size() == 0)
248 {
249 return null;
250 }
251
252 return display;
253 }
254
255 private void addShowInformation(List<String> column1, List<String> column2, AMQMessage msg,
256 String title, boolean routing, boolean headers, boolean messageHeaders)
257 {
258 List<QueueEntry> single = new LinkedList<QueueEntry>();
259 single.add(new QueueEntryImpl(null,msg, Long.MIN_VALUE));
260
261 List<List> routingData = super.createMessageData(null, single, headers, routing, messageHeaders);
262
263 //Reformat data
264 if (title != null)
265 {
266 column1.add(title);
267 column2.add("");
268 column1.add(Console.ROW_DIVIDER);
269 column2.add(Console.ROW_DIVIDER);
270 }
271
272 // look at all columns in the routing Data
273 for (List item : routingData)
274 {
275 // the item should be:
276 // Title
277 // *divider
278 // value
279 // otherwise we can't reason about the correct value
280 if (item.size() == 3)
281 {
282 //Filter out the columns we are not interested in.
283
284 String columnName = item.get(0).toString();
285
286 if (!(columnName.equals(Show.Columns.ID.name())
287 || columnName.equals(Show.Columns.Size.name())))
288 {
289 column1.add(columnName);
290 column2.add(item.get(2).toString());
291 }
292 }
293 }
294 column1.add(Console.ROW_DIVIDER);
295 column2.add(Console.ROW_DIVIDER);
296 }
297
298 private boolean isPrintable(byte c)
299 {
300 return c > 31 && c < 127;
301 }
302 }
|