We will now discuss the LiveData program mentioned earlier to illustrate the use of these classes. But before that lets see the case of NYSE and NASDAQ once again from previous post:
Consider the case of the NYSE and NASDAQ.
· These stock exchanges stream trade data in real time to brokers.
· Trade data consists of scrip code, trade time, bid price, offer price, volume, the high, low, and opening prices, as well as a few more fields.
· This data is transmitted over a socket connection to the client machine.
· The exchange server writes this data to a modem buffer. The data is sent in raw binary format in order to save bandwidth.
In this case, we would use a ByteArrayOutputStream class to write the data to its internal buffer. This class deals with only raw binary data. To provide the abstraction to higher data types, we would use the DataOutputStream class, which is a wrapper on the ByteArrayOutputStream class.
The DataOutputStream contains many methods to write the primitive data types. By chaining the data output stream to a byte array output stream, we can write the binary form of the trade data into a byte array and then send the entire array in a single packet to the remote machine.
At the client end, we would use ByteArrayInputStream to buffer the data retrieved from a socket connection. You would then use the DataInputStream class to convert the raw data to primitive data types.
In general, whenever we want to extract data values of different types from an array of bytes, we would use these InputStream-derived classes. The LiveData application we will implement here uses such a data buffer; it contains an integer, a character array, a few double values, and a long data type. In C++, we would use memory pointers to reference different parts of the array and perform casting on a group of bytes to extract the data of a particular type. In Java, this type of direct memory referencing is not permitted. And that’s where these InputStream-derived classes come in handy.
Also, none of the methods of the ByteArrayInputStream class throw an IOException. This is because the data comes from an array rather than an actual I/O device.
The complete program is given in here:
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
public class LiveData {
private ByteArrayOutputStream outStream;
public static void main(String args[]) throws IOException { LiveData app = new LiveData();
app.createData();
app.readData();
}
public void createData() {
try {
outStream = new ByteArrayOutputStream(); DataOutputStream writer = new DataOutputStream(outStream); for (int i = 0; i < 10; i++) {
Trade lastTrade = new Trade(i); writer.writeInt(lastTrade.scripCode); writer.write(lastTrade.time); writer.writeDouble(lastTrade.bid); writer.writeDouble(lastTrade.offer); writer.writeDouble(lastTrade.high); writer.writeDouble(lastTrade.low); writer.writeLong(lastTrade.quantity); }
} catch (Exception e) {
System.out.println("Error while writing data to buffer"); }
}
private void readData() {
byte[] timeBuffer = new byte[8];
StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb, Locale.US); ByteArrayInputStream inStream = new ByteArrayInputStream( outStream.toByteArray());
DataInputStream reader = new DataInputStream(inStream); try {
for (int i = 0; i < 10; i++) {
int scripCode = reader.readInt(); reader.read(timeBuffer);
String time = new String(timeBuffer); double bid = reader.readDouble(); double offer = reader.readDouble(); double high = reader.readDouble(); double low = reader.readDouble(); long volume = reader.readLong(); formatter.format(
"ScripCode: %2d" + "\tTime: %s " + "\tBid:$ %05.2f" + "\tOffer:$ %05.2f" + "\tHigh:$ %05.2f" + "\tLow:$ %05.2f" + "\tVolume: %d", scripCode, time, bid, offer, high, low, volume); System.out.println(sb); sb.delete(0, sb.length());
}
} catch (Exception e) {
System.out.println("Error while reading data"); }
}
private class Trade {
private int scripCode;
private byte[] time;
private double bid, offer, high, low;
private long quantity;
public Trade(int i) {
scripCode = i + 1;
time = now("hh:mm:ss").getBytes();
bid = Math.random() * 100;
offer = Math.random() * 100;
high = Math.random() * 100;
low = Math.random() * 100;
quantity = (long) (Math.random() * 100000000);
}
private String now(String dateFormat) {
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); return sdf.format(cal.getTime());
}
}
}
Explanation
- The main function in the LiveData class creates an application instance and calls its two methods: createData and readData.
- The createData method creates a few random trades and writes this data to an output stream.
- The readData method reads this data from a byte array obtained from the preceding output stream and prints the data in proper format on the user console
- The createData method creates an instance of ByteArrayOutputStream and copies its reference to a class variable for use by the readData method:
outStream = new ByteArrayOutputStream();
- We build a DataOutputStream object on top of this output stream:
DataOutputStream writer = new DataOutputStream(outStream);
Using this object reference, we are now able to write data to the underlying byte array using the higher-level primitive data type write methods. We create 10 instances of the Trade class and write its fields using the methods writeInt, writeDouble, and so on:
for (int i = 0; i < 10; i++) {
Trade lastTrade = new Trade(i);
writer.writeInt(lastTrade.scripCode);
writer.write(lastTrade.time);
writer.writeDouble(lastTrade.bid);
The Trade class constructor assigns some random values to its fields during instantiation. The readData method creates an instance of the ByteArrayInputStream on the byte array obtained from the out stream:
ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
We open the DataInputStream on top of it to read the data by using the higher-level data-read functions:
DataInputStream reader = new DataInputStream(inStream);
Manish Kumar
10-Apr-2017