## 5 The New RAM3S
### Generalizing the Message Broker  
The first extension needed for RAM3S is the ability to specify the message broker used to read the input stream and to write the output stream: The original version of RAM3S was hard-coded to use RabbitMQ. Decoupling RAM3S from the message broker avoids binding the underlying frameworks to a specific choice of message broker, so that a factory (MessageBrokerFactory) can be used to obtain a particular message broker that will be automatically used by the frameworks that manage the stream processing. For this, two interfaces were added to RAM3S, so as to manage separately the input and output streams: The Reader is directly connected to the RAM3S Receiver while the Writer is used to store results obtained from the analyze() method of the Analyzer (the output of the Analyzer:analyze() method has also been modified to return a MM object instance rather than a boolean value). Figure 3 displays an UML diagram of the main interfaces created in RAM3S. The Receiver (respectively, the Analyzer) uses the MessageBrokerFactory to obtain a Reader to read MMObjects from (resp., a Writer to write results to). Four methods were included in the Reader (resp. Writer) interface:  
* The constructor offers the possibility to configure the message broker through an array of strings, typically obtained from the Receiver (the Writer constructor has an additional parameter to possibly specify a name for the output stream). * The start() and stop() methods are included in case a specific message broker, implementing the interface, requires some startup/shutdown operation. * The read() (resp.write()) method reads (resp. writes) a byte array from/to the stream. The use of a specific message broker only requires to exploit the particular Message BrokerFactory interface provided by RAM3S, so as to obtain the Reader/Writer objects for the chosen message broker (see Figure 4 for the case of Kafka). The creation of a particular application is obtained by implementing the Application Factory interface to produce the specific Receiver/Analyzer objects. In the Receiver, the read() method specifies how a sequence of bytes can be used to create a particular MMObject, while the analyze() method of the Analyzer performs the analysis of a single MMObject, producing the result in form of a sequence of bytes that can be written on the Writer. Figure 5 shows the UML diagram for the face recognition application. Figure 4: UML diagram of the message broker factory for the Kafka case. Figure 3: UML diagram of the main RAM3S interfaces. By introducing the Reader and Writer abstractions, we also allow the new version of RAM\({}^{3}\)S to be recursively cascaded so as to perform more complex operations on the input stream. Suppose that the first operation performed on the input MM stream produces a new stream of MM objects; this output stream could be redirected as input to another RAM\({}^{3}\)S instance, analyzing such an MM stream to produce the result: For example, the first input stream could be a stream of documents that are first analyzed to extract images they contain. Such images are then fed (as a new MM stream) to a second RAM\({}^{3}\)S instance that performs some, say, classification task. This was clearly not possible with the original RAM\({}^{3}\)S, where all computations were relegated on a single node (where the analyze() method was run). As another example, consider the use case originally presented in RAM\({}^{3}\)S, concerning the recognition of known faces from incoming videos for security purposes (see also Section 1.1). In this case, for each image in the incoming stream, the RAM\({}^{3}\)S Analyzer should (i) check whether it contains at least a face and (ii) compare all faces in the image to the \"known\" ones. As also acknowledged in [2], the first task is the most computationally expensive, mostly because, in the considered domain, detection should be particularly accurate so as to avoid the mis-detection of a face.