mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-08 20:12:23 +00:00
this to avoid any flooding of TCP sockets by the RabbitMQ server when the client isn't able to consume traffic fast enough
86 lines
4.3 KiB
Java
Executable File
86 lines
4.3 KiB
Java
Executable File
package com.sap.sse.replication.impl;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.PipedInputStream;
|
|
import java.io.PipedOutputStream;
|
|
import java.util.UUID;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
import com.rabbitmq.client.Channel;
|
|
import com.rabbitmq.client.ConsumerCancelledException;
|
|
import com.rabbitmq.client.QueueingConsumer;
|
|
import com.rabbitmq.client.QueueingConsumer.Delivery;
|
|
import com.rabbitmq.client.ShutdownSignalException;
|
|
import com.sap.sse.common.impl.NamedImpl;
|
|
|
|
/**
|
|
* Input stream that reads messages from a RabbitMQ queue filled by a {@link RabbitOutputStream} and lets a client read
|
|
* this stream from {@link #getInputStream()}.
|
|
*
|
|
* @author Simon Marcel Pamies
|
|
* @author Axel Uhl
|
|
*/
|
|
public class RabbitInputStreamProvider extends NamedImpl {
|
|
private static final Logger logger = Logger.getLogger(RabbitInputStreamProvider.class.getName());
|
|
private static final long serialVersionUID = 1342935135386887494L;
|
|
|
|
private final PipedInputStream clientReadsFromThis;
|
|
private final PipedOutputStream messagesAreWrittenToThis;
|
|
|
|
public RabbitInputStreamProvider(Channel channel, String queueName) throws IOException {
|
|
this(channel, queueName, /* name */ UUID.randomUUID().toString());
|
|
}
|
|
|
|
public RabbitInputStreamProvider(Channel channel, String queueName, String name) throws IOException {
|
|
super(name);
|
|
assert name != null;
|
|
messagesAreWrittenToThis = new PipedOutputStream();
|
|
clientReadsFromThis = new PipedInputStream(messagesAreWrittenToThis);
|
|
final QueueingConsumer messageConsumer = new QueueingConsumer(channel);
|
|
channel.basicQos(1); // with "manual" acknowledgement, process one message at a time, avoiding overloading inbound socket
|
|
channel.basicConsume(queueName, /* auto-ack */ false, messageConsumer);
|
|
new Thread(getClass().getSimpleName()) {
|
|
@Override
|
|
public void run() {
|
|
while (true) {
|
|
try {
|
|
final Delivery delivery = messageConsumer.nextDelivery();
|
|
byte[] bytesFromMessage = delivery.getBody();
|
|
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), /* multiple */ false);
|
|
if (RabbitOutputStream.startsWithTerminationCommand(bytesFromMessage, bytesFromMessage.length)) {
|
|
if (bytesFromMessage.length == RabbitOutputStream.TERMINATION_COMMAND.length) {
|
|
// received exactly TERMINATION_COMMAND
|
|
messagesAreWrittenToThis.close();
|
|
break;
|
|
} else {
|
|
// more to come; skip one byte which is the "escape" symbol ensuring that the message
|
|
// sizes don't match
|
|
byte[] newBytesFromMessage = new byte[bytesFromMessage.length - 1];
|
|
System.arraycopy(bytesFromMessage, 0, newBytesFromMessage, 0,
|
|
RabbitOutputStream.TERMINATION_COMMAND.length);
|
|
System.arraycopy(bytesFromMessage, RabbitOutputStream.TERMINATION_COMMAND.length + 1,
|
|
newBytesFromMessage, RabbitOutputStream.TERMINATION_COMMAND.length,
|
|
bytesFromMessage.length - RabbitOutputStream.TERMINATION_COMMAND.length - 1);
|
|
bytesFromMessage = newBytesFromMessage;
|
|
}
|
|
}
|
|
messagesAreWrittenToThis.write(bytesFromMessage);
|
|
} catch (ShutdownSignalException | ConsumerCancelledException e) {
|
|
logger.log(Level.INFO, "Problem with message queue "+getName(), e);
|
|
break;
|
|
} catch (InterruptedException e) {
|
|
logger.log(Level.WARNING, "Reading of next message in stream "+getName()+" Interrupted; continuing", e);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}.start();
|
|
}
|
|
|
|
public InputStream getInputStream() {
|
|
return clientReadsFromThis;
|
|
}
|
|
} |