Implementing asynchronous publish subscribe topic in tibco ems - asynchronous

my scenario is that me as a movie distributor, need to update my clients on new movies, I publish this information on a topic with durable subscribers and clients who want to buy the movie will express their interest.
However, this is where things go south, my implementation of the publisher stops listening as soon as it receives the first reply. Any help would be greatly appreciated. Thank you.
request(Message message)
Sends a request and waits for a reply.
The temporary topic is used for the JMSReplyTo destination; the first reply is returned, and any following replies are discarded.
https://docs.oracle.com/javaee/6/api/javax/jms/TopicRequestor.html

First thing first... I have questions regarding the scenario. Is this some kind of test/exercice, or are we talking about a real world scenario ?
Are all client interested in the movie SEPARATE topic subscribers ? How does that scale ? I the plan to have a topic for every movie, and possible interested parties declaring durable subscribers (one each, for every movie) ? This seems to be abuse of durable subcribers... I would suggest using ONLY one subscriber (in system B) to a "Movie Released" event/topic (from system A), and have some code (in system B) reading all the clients from a DB to send emails/messages/whatever. (If system A and B are the same, it may or not be a good idea to use EMS at all... depends.)
If it is not an exercise, I must comment : Don't use a MOM (EMS, ActiveMQ) to do a DBMS' (Oracle, PostGreSQL) work !
With the disclaimer section done, I suggest an asynchronous subscription approach (These two clips are taken for the EMS sample directory. File tibjmsAsyncMsgConsumer.java).
Extract from the constructor (The main class must implements ExceptionListener, MessageListener):
ConnectionFactory factory = new com.tibco.tibjms.TibjmsConnectionFactory(serverUrl);
/* create the connection */
connection = factory.createConnection(userName,password);
/* create the session */
session = connection.createSession();
/* set the exception listener */
connection.setExceptionListener(this);
/* create the destination */
if (useTopic)
destination = session.createTopic(name);
else
destination = session.createQueue(name);
System.err.println("Subscribing to destination: "+name);
/* create the consumer */
msgConsumer = session.createConsumer(destination);
/* set the message listener */
msgConsumer.setMessageListener(this);
/* start the connection */
connection.start();
The method is then called every time a message arrives.
public void onMessage(Message msg)
{
try
{
System.err.println("Received message: " + msg);
}
catch (Exception e)
{
System.err.println("Unexpected exception in the message callback!");
e.printStackTrace();
System.exit(-1);
}
}

You want to continue reading messages in a loop. Here is an example:
/* read messages */
while (true)
{
/* receive the message */
msg = msgConsumer.receive();
if (msg == null)
break;
if (ackMode == Session.CLIENT_ACKNOWLEDGE ||
ackMode == Tibjms.EXPLICIT_CLIENT_ACKNOWLEDGE ||
ackMode == Tibjms.EXPLICIT_CLIENT_DUPS_OK_ACKNOWLEDGE)
{
msg.acknowledge();
}
System.err.println("Received message: "+ msg);
}
You may want to also consider a possible issue with durable consumers. If your consumers never pick up their messages, storage will continue to grow at the server side. For this reason you may want to send your messages with an a expiration time, and/or limit maximum number of messages (or size in KB/MB/GB) of the JMS topics you are using.

Related

Spring Kafka Manual Immediate Acknowledgement along with SeekToCurrentErrorHandler

I am referring this answer:
https://stackoverflow.com/questions/56728833/seektocurrenterrorhandler-deadletterpublishingrecoverer-is-not-handling-deseria#:~:text=It%20works%20fine%20for%20me%20(note%20that%20Boot%20will%20auto-configure%20the%20error%20handler)...
Can we add manual immediate acknowledgement like below:
#KafkaListener(id = "so56728833", topics = "so56728833")
public void listen(Foo in, Acknowledgment ack {
System.out.println(in);
if (in.getBar().equals("baz")) {
throw new IllegalStateException("Test retries");
}
ack.acknowledge();
}
I want this because of following scenario:
Let's say I have processed 100 messages, now while processing next 10 records, my consumer gets down after processing 4 messages. In this case, rebalance will get triggered and this 4 messages will be processed again because I have not committed my offset.
Please help.
Yes, you can use manual immediate here - you can also use AckMode.RECORD and the container will automatically commit each offset after the record has been processed.
https://docs.spring.io/spring-kafka/docs/current/reference/html/#committing-offsets

Android <-> iOS Bluetooth LE application, can't write to characteristic

I'm developing a ble-based native local multiplayer plugin for Unity (for both Android and iOS). I use a single service, with a single characteristic with rw permissions. I've managed to make Android<->Android and iOS<->iOS work all right, but I'm having a rough time trying to make Android<->iOS work. Specifically, it's the 'iOS as Peripheral, Android as Central' combination the one that keeps me up at night. After many hours of fiddling, testing, googling and trying, I have very much pinned down the problem to this:
From the Android side, if I don't subscribe to the characteristic, a call to BluetoothGatt#writeCharacteristic(characteristic), like this:
String str = "the data";
xferCharacteristic.setValue(str.getBytes("UTF-8"));
mGatt.writeCharacteristic(xferCharacteristic);
will return 'true' and succeed, and the peripheralManager:didReceiveWriteRequests: callback will be called on the iOS side where I can manipulate the precious received data as I see fit. So far so good. But, if I try to update a characteristic from the iOS end, the Android central won't get notified (the callback BluetoothGattCallback#onCharacteristicChanged should be called, but it isn't), since it did not subscribe to the characteristic.
If I make the Android central subscribe to the characteristic offered by the iOS peripheral, by means of this section of code:
First, connect to the iOS peripheral with
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice btDevice = result.getDevice();
mGatt = device.connectGatt(appContext, false, mGattCallback);
...
with mGattCallback an instance of BLEGattCallback which will handle the onServicesDiscovered callback:
public class BLEGattCallback extends BluetoothGattCallback {
private static final UUID CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
List<BluetoothGattService> services = gatt.getServices();
for(BluetoothGattService s : services) { // foreach service...
if(UUID.fromString(MyServiceUUID).equals(s.getUuid())) { // just the one I want...
List<BluetoothGattCharacteristic> characteristics = s.getCharacteristics();
for(BluetoothGattCharacteristic c : characteristics) { // foreach characteristic...
if(c.getUuid().toString().equals(BLEManager.FGUUIDXferQueueString)) { // just the char. I want...
c.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
for (BluetoothGattDescriptor descriptor : c.getDescriptors()) {
if(descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID)) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
gatt.setCharacteristicNotification(c, true);
}
}
}
}
}
This makes the Android central correctly subscribe for the characteristic (the callback method peripheralManager:central:didSubscribeToCharacteristic: is called on the iOS peripheral), BUT, if i do this, the call to mGatt.writeCharacteristic(xferCharacteristic) will return 'false' and no data will be written to the peripheral, so it's a kind of can-only-write or can-only-notify-update situation.
I have unsuccessfully tried to find out the meaning of writeCharacteristic returning 'false', to no avail (seriously, an error code would help a lot).
I've tried a lot of different combinations, values, etc... but, bottom line: as soon as I call gatt.writeDescriptor subsequent calls to writeCharacteristic will fail, and if I don't call gatt.writeDescriptor the android central won't subscribe.
I'm pretty much stuck here. Any help appreciated. Thanks a lot.
Classic issue. You must wait for the operation to complete before you can issue another one. See Android BLE BluetoothGatt.writeDescriptor() return sometimes false.
Thanks to the received hint, this issue has been solved. These are the changes I made to the code:
The Android client must wait for the writeDescriptor(...) request to finish before issuing a writeCharacteristic(...) command. For that, I had to #Override the method onDescriptorWrite on my BLEGattCallback class, which will be called when the writeDescriptor operation completes. I moved my first writeCharacteristic(...) call here, and now the information is sent to the iOS endpoint (the rest must be flow-controlled). So I'm very happy.

PNaCl: Handle another message while already in 'HandleMessage' function?

I'm using PNaCl and I'm in a situation where first I receive a message that is handled in the 'HandleMessage' function as the normal way, but then in the current HandleMessage execution, I need to wait for a user input that would come from an other message in order to complete the execution.
I'm wondering if this is possible to do that (handling a message while already waiting in the 'HandleMessage' function) ? And if so, can someone give me a trick ?
Thanks !
HandleMessage is currently called on one thread, the main thread. So you cannot receive a message while you are handling another message.
We typically suggest you spawn a new thread to do your work, and leave the main thread to handle messages, and queue them for the new thread to handle. Take a look at the nacl_io_demo example in the SDK for an example of this technique (found in examples/demo/nacl_io).
Another solution is to use a state machine; i.e. keep track of your current state in a variable instead of on the stack.
For example:
enum State {
STATE_INIT,
STATE_WAITING_FOR_INPUT,
STATE_DO_OTHER_STUFF,
};
State state_;
virtual void HandleMessage(const pp::Var& var_message) {
switch (state_) {
case STATE_INIT:
if (var_message.AsString() == "first_message") {
state_ = STATE_WAITING_FOR_INPUT;
// Do some work before you need the user input ...
}
break;
case STATE_WAITING_FOR_INPUT:
if (var_message.AsString() == "user_input") {
// Do more work, now that we've received input from the user...
state_ = STATE_DO_OTHER_STUFF;
}
break;
}
}

How to use BufferingForwardingAppender along with EventLog appender to get email alerts?

I need some idea to implement the following requirement in the web application .
I am using log4net in a custom dll to log the errors. I completed the log4net implementation and its working fine.[aspx errors are logged in EventLog and the asp errors are logged in FileAppender] .All the loggerError() methods are in the custom dll.
Now i want to monitor the logging,suppose if there is a situation like the loggerError() method is called more than 20 times in just 5 mins bse of Fatal error or database is down,then i want to track that and send email to admin.
My ideas,
1.Set a timer and count variable to track the number of hits .
2.After each hit check the number of hits and the secs.
3.If it exceeds the threshold limit .then trigger the email...
Not sure how this will work or is there any other way to achieve it.
Thanks in advance
I would suggest writing your own Appender to house this logic. The Appender could be used as a wrapper for another Appender (maybe the SmtpAppender?). The ForwardingAppender looks like a good example of a "wrapper" Appender. As each log message comes in, apply your timing and severity logic. If your criteria are met, forward the message to the "wrapped" Appender.
Alternatively this Appender could contain its own logic for generating and sending email (or whatever notification scheme you would like to use).
This Appender could be configured via the standard log4net configuration mechanism (app.config, separate config, etc) so the time span, error level, error count, could be configurable.
Here is an idea for how something like this might be implemented as an Appender:
public class MultiThresholdNotifyingAppender : AppenderSkeleton
{
private Queue<LoggingEvent> loggingEventQueue = new Queue<LoggingEvent>();
private DateTime windowStart;
public Level LevelThreshold { get; set; }
public TimeSpan WindowLength { get; set; }
public int HitThreshold { get; set; }
public void Append(LoggingEvent loggingEvent)
{
if (loggingEvent.Level < LevelThreshold)
{
if (loggingEventQueue.Count == 0) return;
if (loggingEvent.TimeStamp - windowStart) >= WindowLength)
{
//Error level is less than threshold and the time window has elapsed. Remove any queued LoggingEvents
//until all LoggingEvents in the queue fall within the time window.
while (loggingEventQueue.Count > 0 && loggingEventQueue.Peek().TimeStamp - windowStart >= WindowLength)
{
loggingEventQueue.Dequeue();
}
windowStart = loggingEventQueue.Peek().TimeStamp;
return;
}
}
//If we got here, then the Error level is >= the threshold. We want to save the LoggingEvent and we MIGHT
//want to notify the administrator if the other criteria are met (number of errors within time window).
loggingEventQueue.Enqueue(loggingEvent);
//If this is the first error in the queue, start the time window.
if (loggingEventQueue.Count == 1) windowStart = loggingEvent.TimeStamp;
//Too few messages to qualify for notification.
if (loggingEventQueue.Count < HitThreshold - 1) return;
//Now we're talking! A lot of error messages in a short period of time.
if (loggingEvent.TimeStamp - windowStart >= WindowLength)
{
//Build a notification message for the administrator by concatenating the "rendered" version of each LoggingEvent.
string message = string.Join(Enviroment.NewLine, loggingEventQueue.Select(le => le.RenderedMessage));
SendTheMessage(message);
}
//After sending the message, clear the LoggingEvents and reset the time window.
loggingEventQueue.Clear();
windowStart = loggingEvent.TimeStamp;
}
public void Append(LoggingEvent[] loggingEvents)
{
foreach (var le in loggingEvents)
{
Append(le);
}
}
}
The idea is to configure your threshold (maybe along with your notification method) values on the appender. Configure your loggers to send their messages to this appender, in addition to any other appenders you want to send them to (EventLog, File, etc). As each logging message comes through, this appender can examine it in the context of the configured threshold values and send the notification as necessary.
There could very well be threading issues in this code (unless log4net handles that for you), so you might need a lock when accessing the queue.
Note that this code has not been compiled nor has it been tested.
See this link for some sample custom Appenders in the log4net repository:
http://svn.apache.org/viewvc/logging/log4net/trunk/examples/net/2.0/Appenders/
Since this is for a web application, I would suggest using an Application Variable to keep track of the last 10 errors. The next time an error occurs, replace the oldest error (if necessary to keep the error count under 10) with the new error. Put in some logic to check the dates of the error, and adjust the severity level accordingly.

Unable to broadcast to single connection using Atmosphere runtime

I am using Atmosphere runtime 0.6 Snapshot. Tomcat 7 is logging correctly that I am using the Http11 Nio connector and there is no warning that BlockingIO will be used.
I am trying to send messages to three kinds of channels.
Global Broadcaster - broadcast to all suspended resources. (All)
Broadcast to a particular resource (say, Partner)
Broadcast to current resource (Self)
When a login action occurs, what all do I have to store in session in order to achieve this kind of broadcasting?
Some details of my code are as follows:
My Handler implements AtmosphereHandler
In the constructor, I instantiate the globalBroadcaster as follows:
globalBroadcaster = new DefaultBroadcaster();
On login,
resource.getAtmosphereConfig().getServletContext().setAttribute(name, selfBroadcaster);
where name is the user name from request parameter and selfBroadcaster is a new instance of DefaultBroadcaster.
Here is the code for sendMessageToPartner,
private synchronized void sendMessageToPartner(Broadcaster selfBroadcaster,
AtmosphereResource<HttpServletRequest, HttpServletResponse> resource,String name, String message) {
// this gives the partner's name
String partner= (String) resource.getAtmosphereConfig().getServletContext().getAttribute(name + PARTNER_NAME_TOKEN);
// get partner's broadcaster
Broadcaster outsiderBroadcaster = (Broadcaster) resource
.getAtmosphereConfig().getServletContext()
.getAttribute(partner);
if (outsiderBroadcaster == null) {
sendMessage(selfBroadcaster, "Invalid user " + partner);
return;
}
// broadcast to partner
outsiderBroadcaster.broadcast(" **" + message);
I hope I have given all the required information. I can provide more information if required.
The problem is, the global message gets sent. When message to partner is sent, sometimes it gets blocked, the message is not received in the client at all. This happens consistently after 3-4 messages.
Is there some threading problem? What am I doing wrong?
I hope somebody helps me out with this.
Ok, I figured out how this can be achieved with Atmosphere runtime.
First, I upgraded to 0.7 SNAPSHOT, but I think the same logic would work with 0.6 as well.
So, to create a broadcaster for a single user:
In GET request,
// Use one Broadcaster per AtmosphereResource
try {
atmoResource.setBroadcaster(BroadcasterFactory.getDefault().get());
} catch (Throwable t) {
throw new IOException(t);
}
// Create a Broadcaster based on this session id.
selfBroadcaster = atmoResource.getBroadcaster();
// add to the selfBroadcaster
selfBroadcaster.addAtmosphereResource(atmoResource);
atmoResource.suspend();
When login action is invoked,
//Get this broadcaster from session and add it to BroadcasterFactory.
Broadcaster selfBroadcaster = (Broadcaster) session.getAttribute(sessionId);
BroadcasterFactory.getDefault().add(selfBroadcaster, name);
Now the global broadcaster. The logic here is, you create a broadcaster from the first resource and then add each resource as they log in.
Broadcaster globalBroadcaster;
globalBroadcaster = BroadcasterFactory.getDefault().lookup(DefaultBroadcaster.class, GLOBAL_TOKEN, false);
if (globalBroadcaster == null) {
globalBroadcaster = selfBroadcaster;
} else {
BroadcasterFactory.getDefault().remove(
globalBroadcaster, GLOBAL_TOKEN);
AtmosphereResource r = (AtmosphereResource) session
.getAttribute("atmoResource");
globalBroadcaster.addAtmosphereResource(r);
}
BroadcasterFactory.getDefault().add(globalBroadcaster,
GLOBAL_TOKEN);
Finally, you can broadcast to Single connection or Globally to all connections as follows:
// Single Connection/Session
Broadcaster singleBroadcaster= BroadcasterFactory.getDefault().lookup(
DefaultBroadcaster.class, name);
singleBroadcaster.broadcast("Only for you");
// Global
Broadcaster globalBroadcaster = BroadcasterFactory.getDefault().lookup(DefaultBroadcaster.class,GLOBAL_TOKEN, false);
globalBroadcaster.broadcast("Global message to all");
To send message to partner, just lookup the broadcaster for the partner and do the same as above for single connection.
Hope this helps someone who tries to achieve the same.
There may be better ways of doing this.
I think I will have to use this approach until someone suggests a better solution.

Resources