Dump.java
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 = (ContentChunkbodies.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 % == && 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 += (charch;
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 }