/******************************************************************************* An implementation of the Java Debug Wire Protocol (JDWP) for JOP Copyright (C) 2007 Paulo Abadie Guedes This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *******************************************************************************/ package debug.io; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; /** * EmbeddedOutputStream.java * * A class to embed data into a text stream in a transparent way. * * This class is responsible for writting data to an output * stream, in a format that can later be filtered and removed from * the output. The isolated data can then be recovered and redirected * to another stream for usage. * * The effect is that it actually "embed" data packets inside a text stream. * The receiving stream has to be prepared to accept and filter * those data packets, to avoid them to be printed to the end user * and also to recover its data and handle it. * * This class could easily be used to simulate several distinct streams * over the same text stream, by using distinct tokens for each kind of * stream. Currently there is no such need yet. * To implement this the change would be just to set some methods * as not static and the token as a private field. * Then each object would start with a new token. * * On the other side, the output stream receiving this data would need * to be ready to handle multiple streams as well. This could be achieved * by composing distinct output stream objects, one to filter each * specific type of token. * * @author Paulo Abadie Guedes * * 04/06/2007 - 23:03:38 * */ public class EmbeddedOutputStream extends OutputStream { public static final String DEFAULT_TOKEN = "JDWP_DATA_PREFIX:"; public static final byte[] hexTable = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; private PrintStream printStream; private String token; public EmbeddedOutputStream (OutputStream outputStream) { this(new PrintStream(outputStream)); } public EmbeddedOutputStream (PrintStream outputStream) { this(outputStream, DEFAULT_TOKEN); } public EmbeddedOutputStream (PrintStream outputStream, String token) { printStream = outputStream; this.token = token; } /* (non-Javadoc) * @see java.io.OutputStream#write(int) */ public void write(int data) throws IOException { //------------------------------------------------------------ // WARNING: don't print ANYTHING to the standard output between // the three calls below or the protocol will be broken. // If this is done, the inserted text will be interpreted as // data fields on the other side, which will break the receiver. printStream.print(token); printStream.print("0002"); printHex((byte)data, printStream); //------------------------------------------------------------ } public void testEmbeddedOutputStreamSendBytes() { int index; int num; num = 256; // num = 5; for(index = 0; index < num; index++) { writeByte((byte)index); } // num = 256; // for(index = 0; index < num; index++) // { // printHex((byte) index); // printStream.print(" "); // } // printHex((byte)128); // testShiftRight(); } public void testEmbeddedOutputStream() { int index; int num; num = 0x0fffffff; for(index = 0; index < num; index++) { writeInt(index); } } /** * Send one byte embedded into the regular text flow of a print stream. * * @param data */ public void writeByte(byte data) { //------------------------------------------------------------ // WARNING: don't print ANYTHING to the standard output between // the three calls below or the protocol will be broken. // If this is done, the inserted text will be interpreted as // data fields on the other side, which will break the receiver. printStream.print(token); printStream.print("0002"); printHex(data); //------------------------------------------------------------ } public void writeInt(int data) { //------------------------------------------------------------ // WARNING: don't print ANYTHING to the standard output between // the three calls below or the protocol will be broken. printStream.print(token); // System.err.println("Token delivered."); printStream.print("0008"); // System.err.println("Size delivered."); printIntHex(data); // System.err.println("Data delivered."); //------------------------------------------------------------ } public void printIntHex(int data) { // WARNING this method is used to send embedded data. // Don't print debug information inside it. printHex((byte)((data >> 24)& 0xff)); printHex((byte)((data >> 16)& 0xff)); printHex((byte)((data >> 8)& 0xff)); printHex((byte)(data & 0xff)); } public static void printIntHex(int data, PrintStream stream) { printHex((byte)((data >> 24)& 0xff), stream); printHex((byte)((data >> 16)& 0xff), stream); printHex((byte)((data >> 8)& 0xff), stream); printHex((byte)(data & 0xff), stream); } public void printHex(byte data) { printHex(data, printStream); } public static void printHex(byte data, PrintStream printStream) { // WARNING this method is used to send embedded data. // Don't print debug information inside it. byte nibble; // unsigned shift right operator does not exist for bytes. // The 'and' operation below solved the issue. nibble = hexTable[(data >>> 4) & 0x0F]; printStream.print((char)nibble); nibble = hexTable[data & 0x0F]; printStream.print((char)nibble); } // public void write (byte[] b, int off, int len) // throws IOException, NullPointerException, IndexOutOfBoundsException // { // if (off < 0 || len < 0 || off + len > b.length) // throw new ArrayIndexOutOfBoundsException (); // // //TODO: check if the length is greater than 9999 and handle it // // printStream.print(token); // if(len < 1000) // { // printStream.print("0"); // } // if(len < 100) // { // printStream.print("0"); // } // if(len < 10) // { // printStream.print("0"); // } // printStream.print(len); // // for (int i = 0; i < len; ++i) // { // printHex (b[off + i], printStream); // } // } }