rabbitMQ AlreadyClosedException when basicAck(knowledging) - grails

My groovy code uses the Rabbit Native Plugin for grails:
def handleMessage(def body, MessageContext context) {
// With noAck=false, messages must be acknowledged manually with basic.ack.
boolean noAck = false
// send Ack on true and nack on false
if(processMessage(new SensorEvent(body))){
context.channel.basicAck(context.getEnvelope().getDeliveryTag(),noAck)
}else{
context.channel.basicNack(context.getEnvelope().getDeliveryTag(), false, false);
}
return ''
}
If I comment out the two lines of code that do the Ack and Nack everything works fine. If I uncomment the basicAck I get the following exception
com.rabbitmq.client.AlreadyClosedException: channel is already closed due to channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - unknown delivery tag 1, class-id=60, method-id=80)
at com.rabbitmq.client.impl.AMQChannel.ensureIsOpen(AMQChannel.java:195)
at com.rabbitmq.client.impl.AMQChannel.transmit(AMQChannel.java:309)
at com.rabbitmq.client.impl.AMQChannel.transmit(AMQChannel.java:303)
at com.rabbitmq.client.impl.ChannelN.basicReject(ChannelN.java:1045)
at com.rabbitmq.client.impl.recovery.RecoveryAwareChannelN.basicReject(RecoveryAwareChannelN.java:72)
at com.rabbitmq.client.impl.recovery.AutorecoveringChannel.basicReject(AutorecoveringChannel.java:354)
I've seen advice saying to use the Subscription.Ack(). There is no Subscription class in the Java/Groovy rabbitMQ.
Any idea why I'm getting the exception?
Edit: since I'm using the Native Plugin I needed to create a consumer that implemented the interface
def handleMessage(def body, MessageContext context)
The subscribing is handled with:
/**
* Consumer configuration.
*/
static rabbitConfig = [
"queue": "my.queueName"
]

This error:
PRECONDITION_FAILED - unknown delivery tag 1,
Means that you are trying to ack a message on a different channel than the one which received said message. Delivery Tags are scoped per channel

Related

How to retrieve messages from Alpakka Mqtt Streaming client?

I was following document for writing a Mqtt client subscriber using alpakka.
https://doc.akka.io/docs/alpakka/3.0.4/mqtt-streaming.html?_ga=2.247958340.274298740.1642514263-524322027.1627936487
After the code marked in bold, I’m not sure how could I retrieve/interact with subscribed messages. Any lead?
Pair<SourceQueueWithComplete<Command>, CompletionStage> run =
Source.<Command>queue(3, OverflowStrategy.fail())
.via(mqttFlow)
.collect(
new JavaPartialFunction<DecodeErrorOrEvent, Publish>() {
#Override
public Publish apply(DecodeErrorOrEvent x, boolean isCheck) {
if (x.getEvent().isPresent() && x.getEvent().get().event() instanceof Publish)
return (Publish) x.getEvent().get().event();
else throw noMatch();
}
})
.toMat(Sink.head(), Keep.both())
.run(system);
SourceQueueWithComplete<Command> commands = run.first();
commands.offer(new Command<>(new Connect(clientId, ConnectFlags.CleanSession())));
commands.offer(new Command<>(new Subscribe(topic)));
session.tell(
new Command<>(
new Publish(
ControlPacketFlags.RETAIN() | ControlPacketFlags.QoSAtLeastOnceDelivery(),
topic,
ByteString.fromString(“ohi”))));
// for shutting down properly
commands.complete();
commands.watchCompletion().thenAccept(done → session.shutdown());
Also, in the following example, it shows how to subscribe to the client but nothing about how to get messages after the subscription.
https://github.com/pbernet/akka_streams_tutorial/blob/master/src/main/scala/alpakka/mqtt/MqttEcho.scala
Will be grateful if anyone knows the solution or can point to any resource which uses the same connector as mqtt client and can retrieve messages.
The code to retrieve messages for the subscriber is hidden in the client method which is used for both publisher and subscriber:
...
//Only the Publish events are interesting for the subscriber
.collect { case Right(Event(p: Publish, _)) => p }
.wireTap(event => logger.info(s"Client: $connectionId received: ${event.payload.utf8String}"))
.toMat(Sink.ignore)(Keep.both)
.run()
https://github.com/pbernet/akka_streams_tutorial/blob/3e4484c5356e55522366e65e42e1741c18830a18/src/main/scala/alpakka/mqtt/MqttEcho.scala#L136
I was struggling with this connector and then tried an example with the one based on Eclipse Paho, which in the end looks better:
https://github.com/pbernet/akka_streams_tutorial/blob/3e4484c5356e55522366e65e42e1741c18830a18/src/main/scala/alpakka/mqtt/MqttPahoEcho.scala#L41
Paul

How can I make a TLS connection using Vala?

I'm trying to figure out how can I make a proper TLS connection using Gio. The Gio documentation says you can create a TLS connection just by setting the tls flag on the SocketClient. Below is a Gio networking sample from the gnome wiki. When I set the tls flag, the TLS layer is configured automatically but validating the certificates fails unless I skip the validation.
Do I have to validate certificates myself or is GLib supposed to do the validation? Can somebody provide a full example on how to use TLS in Vala?
var host = "developer.gnome.org";
try {
// Resolve hostname to IP address
var resolver = Resolver.get_default ();
var addresses = resolver.lookup_by_name (host, null);
var address = addresses.nth_data (0);
print (#"Resolved $host to $address\n");
// Connect
var client = new SocketClient ();
client.set_tls(true);
// skips certificate validation
client.set_tls_validation_flags( 0 );
var conn = client.connect (new InetSocketAddress (address, 443));
print (#"Connected to $host\n");
// Send HTTP GET request
var message = #"GET / HTTP/1.1\r\nHost: $host\r\n\r\n";
conn.output_stream.write (message.data);
print ("Wrote request\n");
// Receive response
var response = new DataInputStream (conn.input_stream);
var status_line = response.read_line (null).strip ();
print ("Received status line: %s\n", status_line);
} catch (Error e) {
stderr.printf ("%s\n", e.message);
}
And another thing I want to ask is; when I run the code above I get this output:
Resolved developer.gnome.org to 8.43.85.14
Connected to developer.gnome.org
Wrote request
Received status line: HTTP/1.1 200 OK
But when I try to connect 'developer.mozilla.org', I'm getting the following error:
Resolved developer.mozilla.org to 54.192.235.2
Error performing TLS handshake: A packet with illegal or unsupported version was received.
Can anybody tell me the reason why I am getting this error? (By the way the version of GLib installed on my system is 2.64.6)
What you're doing so far is mostly correct, but you will probably want to do a little bit more to handle potential certificate errors during the TLS handshaking (see below).
Do I have to validate certificates myself or is GLib supposed to do the validation?
Note that SocketClient.set_tls_validation_flags is deprecated. To handle validation errors you can connect to the accept_certificate signal on the TlsClientConnection prior to handshaking:
var client = new SocketClient ();
client.set_tls(true);
client.event.connect ((SocketClientEvent event, SocketConnectable connectable, IOStream? connection) => {
if (event == SocketClientEvent.TLS_HANDSHAKING) {
((TlsClientConnection) connection).accept_certificate.connect ((peer_cert, errors) => {
// Return true to accept, false to reject
});
}
});
The errors are GLib.TlsCertificateFlags, so you'll want to determine which (if any) are acceptable. Ideally if there are any errors you would reject the certificate altogether, but if you want to allow self-signed certificates for example, that is possible this way.
You can simply check against the flags to see which ones are included in the errors:
TlsCertificateFlags[] flags = new TlsCertificateFlags[] {
TlsCertificateFlags.BAD_IDENTITY,
TlsCertificateFlags.EXPIRED,
TlsCertificateFlags.GENERIC_ERROR,
TlsCertificateFlags.INSECURE,
TlsCertificateFlags.NOT_ACTIVATED,
TlsCertificateFlags.REVOKED,
TlsCertificateFlags.UNKNOWN_CA
};
foreach (var flag in flags) {
if ((errors & flag) != 0) {
// The flag was included in the errors - respond accordingly
}
}
But when I try to connect 'developer.mozilla.org', I'm getting the
following error:
Resolved developer.mozilla.org to 54.192.235.2 Error performing TLS
handshake: A packet with illegal or unsupported version was received.
Can anybody tell me the reason why I am getting this error? (By the
way the version of GLib installed on my system is 2.64.6)
This is probably due to developer.mozilla.org using an old implementation of TLS (probably 1.0 or 1.1). These were disabled in GLib networking as of 2.64.x according to this bug report: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=954742
They may have upgraded to TLS 1.2 since you posted this question - I just did a quick test and connected using TLSv1.2 successfully.
Try run the program with export G_MESSAGES_DEBUG=all for full debug messages.
Also, for a full working example of code that uses TLS written in Vala via GIO, check out the code for this Gemini browser: https://github.com/koyuspace/fossil/blob/main/src/util/connection_helper.vala
I hope that this is somewhat useful to you.

How to properly configure SQS without using SNS topics in MassTransit?

I'm having some issues configuring MassTransit with SQS. My goal is to have N consumers which create N queues and each of them accept a different message type. Since I always have a 1 to 1 consumer to message mapping, I'm not interested in having any sort of fan-out behaviour. So publishing a message of type T should publish it directly to that queue. How exactly would I configure that? This is what I have so far:
services.AddMassTransit(x =>
{
x.AddConsumers(Assembly.GetEntryAssembly());
x.UsingAmazonSqs((context, cfg) =>
{
cfg.Host("aws", h =>
{
h.AccessKey(mtSettings.AccessKey);
h.SecretKey(mtSettings.SecretKey);
h.Scope($"{mtSettings.Environment}", true);
var sqsConfig = new AmazonSQSConfig() { RegionEndpoint = RegionEndpoint.GetBySystemName(mtSettings.Region) };
h.Config(sqsConfig);
var snsConfig = new AmazonSimpleNotificationServiceConfig()
{ RegionEndpoint = RegionEndpoint.GetBySystemName(mtSettings.Region) };
h.Config(snsConfig);
});
cfg.ConfigureEndpoints(context, new BusEnvironmentNameFormatter(mtSettings.Environment));
});
});
The BusEnvironmentNameFormatter class overrides KebabCaseEndpointNameFormatter and adds the environment as a prefix, and the effect is that all the queues start with 'dev', while the h.Scope($"{mtSettings.Environment}", true) line does the same for topics.
I've tried to get this working without configuring topics at all, but I couldn't get it working without any errors. What am I missing?
The SQS docs are a bit thin, but is at actually possible to do a bus.Publish() without using sns topics or are they necessary? If it's not possible, how would I use bus.Send() without hardcoding queue names in the call?
Cheers!
Publish requires the use of topics, which in the case of SQS uses SNS.
If you want to configure the endpoints yourself, and prevent the use of topics, you'd need to:
Set ConfigureConsumeTopology = false – this prevents topics from being created and connected to the receive endpoint queue.
Set PublishFaults = false – this prevents fault topics from being created when a consumer throws an exception.
Don't call Publish, because, obviously that will create a topic.
If you want to somehow establish a convention for your receive endpoint names that aligns with your ability to send messages, you could create your own endpoint name formatter that would use message types and then use those same names to call GetSendEndpoint using the queue:name short name syntax to Send messages directly to those queues.

How to use bluetooth devices and FIWARE IoT Agent

I would like to use my bluetooth device (for example I'm going to create an app to be installed in a tablet) to send data (set of attributes) in Orion Context Broker via IoT Agent.
I'm looking for the FIWARE IoT Agent and probably I've to use IoT Agent LWM2M. Is it correct?
Thanks in advance and regards.
Pasquale
Assuming you have freedom of choice, you probably don't need an IoT Agent for that, you just need a service acting as a bluetooth receiver which can receive your message and pass it on using a recognisable transport.
For example, you can receive data using the following Stack Overflow answer
You can then extract the necessary information to identify the device and the context to be updated.
You can programmatically send NGSI requests in any language capable of HTTP - just generate a library using the NGSI Swagger file - an example is shown in the tutorials
// Initialization - first require the NGSI v2 npm library and set
// the client instance
const NgsiV2 = require('ngsi_v2');
const defaultClient = NgsiV2.ApiClient.instance;
defaultClient.basePath = 'http://localhost:1026/v2';
// This is a promise to make an HTTP PATCH request to the /v2/entities/<entity-id>/attr end point
function updateExistingEntityAttributes(entityId, body, opts, headers = {}) {
return new Promise((resolve, reject) => {
defaultClient.defaultHeaders = headers;
const apiInstance = new NgsiV2.EntitiesApi();
apiInstance.updateExistingEntityAttributes(
entityId,
body,
opts,
(error, data, response) => {
return error ? reject(error) : resolve(data);
}
);
});
}
If you really want to do this with an IoT Agent, you can use the IoT Agent Node lib and and create your own IoT Agent

When using the Grails RabbitMQ plugin, how do you set message headers/properties when sending messages?

When consuming messages using the plugin, you can access the raw Message and its headers/properties.
When sending messages using the rabbitSend method, it appears from the documentation (http://grails-plugins.github.com/grails-rabbitmq/docs/manual/ref/All%20Classes/rabbitSend.html) that you can only set the exchange name, routing key and message body.
How can these headers/properties be set when sending a message using the rabbitSend method?
At present, it looks like you need to use the underlying rabbitTemplate.convertAndSend() method. The link to the RabbitTemplate Javadoc in the plugin's documentation is broken at the moment, it should point to http://static.springsource.org/spring-amqp/api/org/springframework/amqp/rabbit/core/RabbitTemplate.html
I found an example of setting the message properties using the rabbitTemplate.convertAndSend() method on the Grails JIRA http://jira.grails.org/browse/GPRABBITMQ-7
rabbitTemplate.convertAndSend "amq.direct", "work", payload, ({ Message msg ->
msg.messageProperties.replyTo = new Address("work.reply")
return msg
} as MessagePostProcessor)

Resources