A software architecture is a set of components connected in standardized ways. For example, you are probably familiar with the idea of a client-server architecture. In that type of architecture there are typically one server component and many client components. The clients are usually connected to the server and communicate in a request/response fashion. That is, the client requests something from the server and the server responds to the client, fulfilling the request.
Your task is to develop a generic software pipe and filter architecture in Java. A pipe and filter architecture is made up of components called filters, connected in a chain. That is, the output of one filter serves as the input to the next filter in the chain. A filter has exactly one input stream and one output stream. Filters may process/transform the data as it flows along the chain. The actual effect of passing data along the chain depends on which filters make up the chain and the order in which they are connected.
To work properly, applications that use this architecture must not connect the filters in cycles (i.e. the directed graph of filters with edges showing the connections from the output of each filter to the input of another filter does not have a cycle). Also, filters must not communicate with each other except through their input and output streams (that includes reading and writing files).
Consider the following example:
C1 - reads a stream of characters, interprets them as integers, computes the running sum of the last 3 integers, and outputs the averages as a stream of characters.
C2 - reads a stream of characters, interprets them as integers, computes the square of each integer, and outputs the squares as a stream of characters.
C3 - reads a stream of characters, interprets them as integers, deletes any value over 1,000,000 , and outputs the remaining integers as a stream of characters.
Connections: C1's input stream is a file and it's output stream is connected to the input stream of C2. C2's output stream is connected to the input stream of C3. The output stream of C3 is System.out.
Notice that since the filters all take character streams as input and have no dependencies on each other, they can be reordered to produce a different effect.
Example 2:
Connections: C2's input stream is a file and it's output stream is connected to the input stream of C1. C1's output stream is connected to the input stream of C3. The output stream of C3 is System.out.
Each filter should consist of three threads, two buffers, one input stream and one output stream. The read thread takes data from the filter's input stream and places it in the input buffer. The compute thread removes data from the input buffer, processes/transforms it, and writes its output to the output buffer. The write thread removes data from the output buffer and writes it to the output stream.
All threads must respect normal buffer semantics. That is, they must not write to a full buffer, read from an empty buffer, nor lose, corrupt or duplicate data items. You may assume that all data items are of type char, and the filter is responsible for conversion to other primitive data types as specified.
Here are the interfaces for the FilterImpl and FilterBufferImpl classes that you must write:
public interface Filter {
void setConnection(java.io.Reader in, java.io.Writer out);
java.io.Reader getInputConnection();
java.io.Writer getOutputConnection();
FilterBuffer getInputBuffer();
FilterBuffer getOutputBuffer();
void startFilter(); // start computing
}
public class FilterImpl implements Filter {
FilterImpl(java.io.Reader in, java.io.Writer out, int maxReadBufSize, int maxWriteBufSize, Runnable compute);
}
public interface FilterBuffer {
char[] read (); // returns current contents of buffer and clears it. Returns a zero-length array if its internal buffer is empty and the FilterBuffer has been closed
void write(char[] str) throws bufTooSmallException; // write str to buffer if str.length is less than maxBufferSize, otherwise throws exception
int getMaxBufferSize();
void close(); // informs the FilterBuffer that no more data will be written to it
void flush(); // doesn't return until the buffer is empty
}
public class FilterBufferImpl implements FilterBuffer {
FilterBufferImpl(int maxSize);
}
You must also write filters for the following applications: