SQS AWS - message.acknowledge - exception with acknowledge mode: UNORDERED_ACKNOWLEDGE - amazon-web-services

I was running the SyncMessageReceiverUnorderedAcknowledge.java program, exactly as written on: http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/jmsclient.html#jmsclient-ackmode
public class SyncMessageReceiverUnorderedAcknowledge {
// Visibility time-out for the queue. It must match to the one set for the queue for this example to work.
private static final long TIME_OUT_SECONDS = 1;
public static void main(String args[]) throws JMSException, InterruptedException {
// Create the configuration for the example
ExampleConfiguration config = ExampleConfiguration.parseConfig("SyncMessageReceiverUnorderedAcknowledge", args);
// Setup logging for the example
ExampleCommon.setupLogging();
// Create the connection factory based on the config
SQSConnectionFactory connectionFactory =
SQSConnectionFactory.builder()
.withRegion(config.getRegion())
.withAWSCredentialsProvider(config.getCredentialsProvider())
.build();
// Create the connection
SQSConnection connection = connectionFactory.createConnection();
// Create the queue if needed
ExampleCommon.ensureQueueExists(connection, config.getQueueName());
// Create the session with unordered acknowledge mode
Session session = connection.createSession(false, **SQSSession.UNORDERED_ACKNOWLEDGE**);
// Create the producer and consume
MessageProducer producer = session.createProducer(session.createQueue(config.getQueueName()));
MessageConsumer consumer = session.createConsumer(session.createQueue(config.getQueueName()));
// Open the connection
connection.start();
// Send two text messages
sendMessage(producer, session, "Message 1");
sendMessage(producer, session, "Message 2");
// Receive a message and don't acknowledge it
receiveMessage(consumer, false);
// Receive another message and acknowledge it
receiveMessage(consumer, true);
// Wait for the visibility time out, so that unacknowledged messages reappear in the queue
System.out.println("Waiting for visibility timeout...");
Thread.sleep(TimeUnit.SECONDS.toMillis(TIME_OUT_SECONDS));
// Attempt to receive another message and acknowledge it. This will result in receiving the first message since
// we have acknowledged only the second message. In the UNORDERED_ACKNOWLEDGE mode, all the messages must
// be explicitly acknowledged.
receiveMessage(consumer, true);
// Close the connection. This will close the session automatically
connection.close();
System.out.println("Connection closed.");
}
/**
* Sends a message through the producer.
*
* #param producer Message producer
* #param session Session
* #param messageText Text for the message to be sent
* #throws JMSException
*/
private static void sendMessage(MessageProducer producer, Session session, String messageText) throws JMSException {
// Create a text message and send it
producer.send(session.createTextMessage(messageText));
}
/**
* Receives a message through the consumer synchronously with the default timeout (TIME_OUT_SECONDS).
* If a message is received, the message is printed. If no message is received, "Queue is empty!" is
* printed.
*
* #param consumer Message consumer
* #param acknowledge If true and a message is received, the received message is acknowledged.
* #throws JMSException
*/
private static void receiveMessage(MessageConsumer consumer, boolean acknowledge) throws JMSException {
// Receive a message
Message message = consumer.receive(TimeUnit.SECONDS.toMillis(TIME_OUT_SECONDS));
if (message == null) {
System.out.println("Queue is empty!");
} else {
// Since this queue has only text messages, cast the message object and print the text
System.out.println("Received: " + ((TextMessage) message).getText());
// Acknowledge the message if asked
if (acknowledge) **message.acknowledge();**
}
}
}
When the below code is reached:
// Create the session with unordered acknowledge mode
Session session = connection.createSession(false,SQSSession.UNORDERED_ACKNOWLEDGE);
// Acknowledge the message if asked
if (acknowledge) message.acknowledge();
I get the following exception:
Exception in thread "main" java.lang.NoSuchMethodError: com.amazonaws.services.sqs.AmazonSQS.deleteMessage(Lcom/amazonaws/services/sqs/model/DeleteMessageRequest;)V
at com.amazon.sqs.javamessaging.AmazonSQSMessagingClientWrapper.deleteMessage(AmazonSQSMessagingClientWrapper.java:127)
at com.amazon.sqs.javamessaging.acknowledge.UnorderedAcknowledger.acknowledge(UnorderedAcknowledger.java:42)
at com.amazon.sqs.javamessaging.message.SQSMessage.acknowledge(SQSMessage.java:883)
at sample.sqs.SyncMessageReceiverUnorderedAcknowledge.receiveMessage(SyncMessageReceiverUnorderedAcknowledge.java:116)
at sample.sqs.SyncMessageReceiverUnorderedAcknowledge.main(SyncMessageReceiverUnorderedAcknowledge.java:67)
I am running with the following gradle dependencies:
compile("com.amazonaws:aws-java-sdk-sqs:1.11.13")
compile("com.amazonaws:amazon-sqs-java-messaging-lib:1.0.0")
I debugged the code, and all the aws amazon java classes looks perfect.
In addition, I created a new program that will run isolated deleteMessage.
With deleteMessage(DeleteMessageRequest deleteMessageRequest) - I get the same exception.
But With DeleteMessageBatchResult deleteMessageBatch(DeleteMessageBatchRequest deleteMessageBatchRequest) - Which is in the same classes - It works!
I cleaned the gradle cache folder, downloaded again all the jars, vlean, build, but I get the same results :-(
Help will be highly appreciated.

https://github.com/awslabs/amazon-sqs-java-messaging-lib/issues/22
I changed gradle dependencies:
compile("com.amazonaws:aws-java-sdk-sqs:1.9.6")
And now it works perfect.
Opening pom.xml of amazon-sqs-java-messaging-lib project, I can see:
<properties> <aws-java-sdk.version>1.9.6</aws-java-sdk.version> </properties>
I wonder when do AWS team plan to change it to the latest one (e.g. 1.11.13)? or one of the latest.

Related

PubSub "Connection reset by peer" on gcp

I encountered the exception.
"System.IO.IOException: Unable to read data from the transport connection: Connection reset by peer.\n ---> System.Net.Sockets.SocketException (104): Connection reset by peer\n --- End of inner exception stack trace ---\n at Google.Cloud.PubSub.V1.SubscriberClientImpl.SingleChannel.HandleRpcFailure(Exception e)\n at Google.Cloud.PubSub.V1.SubscriberClientImpl.SingleChannel.HandlePullMoveNext(Task initTask)\n at Google.Cloud.PubSub.V1.SubscriberClientImpl.SingleChannel.StartAsync()\n at Google.Cloud.PubSub.V1.Tasks.ForwardingAwaiter.GetResult()\n at Google.Cloud.PubSub.V1.Tasks.Extensions.<>c__DisplayClass4_0.<g__Inner|0>d.MoveNext()\n--- End of stack trace from previous location ---\n
"Invoke" function was executed to pull a message from my topic per 5 seconds in the scheduler
public async Task Invoke()
{
var subscriber = await SubscriberClient.CreateAsync(CreateSubscriptionName());
await subscriber.StartAsync((msg, cancellationToken) =>
{
//....
return Task.FromResult(SubscriberClient.Reply.Ack);
});
await subscriber.StopAsync(CancellationToken.None);
}
How did I fix this ?
Thanks!
I've already checked the doc
PublisherClient and SubscriberClient are expensive to create, so when regularly publishing or subscribing to the same topic or subscription then a singleton client instance should be created and used for the lifetime of the application.
But I still don't know how to do ...
I guessed I left too many open connections ?

AWS FIFO SQS queue message is disappearing when I repost the same message even after successfully deleting it

I am facing a strange issue in SQS. Let me simplify my use-case, I have 7 messages in the FIFO queue and my standalone app should keep-on polling the messages in the same sequence for my business case infinitely. For instance, my app read message1 and after some business processing, the app will delete it and repost the same message into the same queue(tail of the queue), and these steps will be continued for the next set of messages endlessly. Here, my expectation is my app will be polling the message continuously and doing the operations based on the messages in the queue in the same sequence, but that's where the problem arises. When the message is read from the queue for the very first time, delete it, and repost the same message into the same queue, even after the successful sendMessageResult, the reposted message is not present in the queue.
I have included the below code to simulate the issue, briefly, Test_Queue.fifo queue with Test_Queue_DLQ.fifo configured as reDrivePolicy is created. At the very first time after creating the queue, the message is posted -> "Test_Message" into Test_Queue.fifo queue(Getting the MessageId in response ) and long-polling the queue to read the message, and after iterating the ReceiveMessageResult#getMessages, deleting the message(Getting MessageId in response). Again, after the successful deletion of the message, the same message is reposted into the tail of the same queue(Getting the MessageId in response). But, the reposted message is not present in the queue. When, I checked the AWS admin console the message count is 0 in the Messages available and Messages in flight sections and the reposted message is not even present in Test_Queue_DLQ.fifo queue. As per the SQS docs, if we delete the message, even if it is present in flight mode should be removed, so reposting the same message should not be an issue. I suspect on SQS side, where they are performing some equals comparison and skipping the same message during in visibleTimeOut interval to avoid deduplication of the same message in the distributed environment, but couldn't get any clear picture.
Code snippet to simulate the above issue
public class SQSIssue {
#Test
void sqsMessageAbsenceIssueTest() {
AmazonSQS amazonSQS = AmazonSQSClientBuilder.standard().withEndpointConfiguration(new AwsClientBuilder
.EndpointConfiguration("https://sqs.us-east-2.amazonaws.com", "us-east-2"))
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(
"<accessKey>", "<secretKey>"))).build();
//create queue
String queueUrl = createQueues(amazonSQS);
String message = "Test_Message";
String groupId = "Group1";
//Sending message -> "Test_Message"
sendMessage(amazonSQS, queueUrl, message, groupId);
//Reading the message and deleting using message.getReceiptHandle()
readAndDeleteMessage(amazonSQS, queueUrl);
//Reposting the same message into the queue -> "Test_Message"
sendMessage(amazonSQS, queueUrl, message, groupId);
ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest()
.withQueueUrl(queueUrl)
.withWaitTimeSeconds(5)
.withMessageAttributeNames("All")
.withVisibilityTimeout(30)
.withMaxNumberOfMessages(10);
ReceiveMessageResult receiveMessageResult = amazonSQS.receiveMessage(receiveMessageRequest);
//Here I am expecting the message presence in the queue as I recently reposted the same message into the same queue after the message deletion
Assertions.assertFalse(receiveMessageResult.getMessages().isEmpty());
}
private void readAndDeleteMessage(AmazonSQS amazonSQS, String queueUrl) {
ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest()
.withQueueUrl(queueUrl)
.withWaitTimeSeconds(5)
.withMessageAttributeNames("All")
.withVisibilityTimeout(30)
.withMaxNumberOfMessages(10);
ReceiveMessageResult receiveMessageResult = amazonSQS.receiveMessage(receiveMessageRequest);
receiveMessageResult.getMessages().forEach(message -> amazonSQS.deleteMessage(queueUrl, message.getReceiptHandle()));
}
private String createQueues(AmazonSQS amazonSQS) {
String queueName = "Test_Queue.fifo";
String deadLetterQueueName = "Test_Queue_DLQ.fifo";
//Creating DeadLetterQueue
CreateQueueRequest createDeadLetterQueueRequest = new CreateQueueRequest()
.addAttributesEntry("FifoQueue", "true")
.addAttributesEntry("ContentBasedDeduplication", "true")
.addAttributesEntry("VisibilityTimeout", "600")
.addAttributesEntry("MessageRetentionPeriod", "262144");
createDeadLetterQueueRequest.withQueueName(deadLetterQueueName);
CreateQueueResult createDeadLetterQueueResult = amazonSQS.createQueue(createDeadLetterQueueRequest);
GetQueueAttributesResult getQueueAttributesResult = amazonSQS.getQueueAttributes(
new GetQueueAttributesRequest(createDeadLetterQueueResult.getQueueUrl())
.withAttributeNames("QueueArn"));
String deadLetterQueueArn = getQueueAttributesResult.getAttributes().get("QueueArn");
//Creating Actual Queue with DeadLetterQueue configured
CreateQueueRequest createQueueRequest = new CreateQueueRequest()
.addAttributesEntry("FifoQueue", "true")
.addAttributesEntry("ContentBasedDeduplication", "true")
.addAttributesEntry("VisibilityTimeout", "600")
.addAttributesEntry("MessageRetentionPeriod", "262144");
createQueueRequest.withQueueName(queueName);
String reDrivePolicy = "{\"maxReceiveCount\":\"5\", \"deadLetterTargetArn\":\""
+ deadLetterQueueArn + "\"}";
createQueueRequest.addAttributesEntry("RedrivePolicy", reDrivePolicy);
CreateQueueResult createQueueResult = amazonSQS.createQueue(createQueueRequest);
return createQueueResult.getQueueUrl();
}
private void sendMessage(AmazonSQS amazonSQS, String queueUrl, String message, String groupId) {
SendMessageRequest sendMessageRequest = new SendMessageRequest()
.withQueueUrl(queueUrl)
.withMessageBody(message)
.withMessageGroupId(groupId);
SendMessageResult sendMessageResult = amazonSQS.sendMessage(sendMessageRequest);
Assertions.assertNotNull(sendMessageResult.getMessageId());
}
}
From Using the Amazon SQS message deduplication ID:
The message deduplication ID is the token used for deduplication of sent messages. If a message with a particular message deduplication ID is sent successfully, any messages sent with the same message deduplication ID are accepted successfully but aren't delivered during the 5-minute deduplication interval.
Therefore, you should supply a different Deduplication ID each time the message is placed back onto the queue.
https://stackoverflow.com/a/65844632/3303074 is fitting, I should have added the SendMessageRequest#withMessageDeduplicationId, but I would like to add few more points to the answer, The technical reason behind the message disappearance is because I have enabled the ContentBasedDeduplication for the queue. Amazon SQS uses an SHA-256 hash to generate the MessageDeduplicationId using the body of the message (but not the attributes of the message) if the MessageDeduplicationId has not been mentioned explicitly when sending the message. When ContentBasedDeduplication is in effect, messages with identical content sent within the deduplication interval are treated as duplicates and only one copy of the message is delivered. So even we add different attributes for the same message repost into the same queue will not work as expected. Adding MessageDeduplicationId helps to solve the issue because even the queue has ContentBasedDeduplication set, the explicit MessageDeduplicationId overrides the generated one.
Code Snippet
SendMessageRequest sendMessageRequest = new SendMessageRequest()
.withQueueUrl(queueUrl)
.withMessageBody(message)
.withMessageGroupId(groupId)
// Adding explicit MessageDeduplicationId
.withMessageDeduplicationId(UUID.randomUUID().toString());
SendMessageResult sendMessageResult = amazonSQS.sendMessage(sendMessageRequest);

Reply a message after receiving data on subscriber in Google PubSub Pull

Is it possible to reply a message once you received a data from Publisher.
It must be a direct reply, once the Publisher published a message.
I'm using Google PubSub Service.
https://cloud.google.com/pubsub/docs/pull
Publisher/Sender (PHP):
$sendToOps =[];
$sendToOps['MESSAGE'] = "my message";
$topicName = env('GOOGLE_CLOUD_TO_OPS_TOPIC');
$pubSub = new PubSubClient();
$topic = $pubSub->topic($topicName);
$ret = $topic->publish([
'attributes'=>$sendToOps
]);
//**********The word "Apple" must output here**********
echo $ret;
//*****************************************************
Subscriber/Receiver (Javascript):
'use strict';
//Get .env File Data
require('dotenv').config({path: '/usr/share/nginx/html/myProject/.env'});
var request = require('request');
var port = process.env.PORT_GATEWAY;
var host = process.env.IP_PUSH;
var test = process.env.TEST_FLAG;
var pubsubSubscription = process.env.GOOGLE_CLOUD_TO_OPS_SUBSCRIPTION;
const keyFilePath= 'public/key/key.json';
// Imports the Google Cloud client library
const {PubSub} = require('#google-cloud/pubsub');
// Creates a client; cache this for further use
const pubSubClient = new PubSub({
keyFilename: keyFilePath
});
function listenForMessages() {
// References an existing subscription
const subscription = pubSubClient.subscription(pubsubSubscription);
// Create an event handler to handle messages
const messageHandler = message => {
console.log(message.attributes);
//*****************************************************
//I want to reply to Sender with the word "Apple" here
//*****************************************************
message.ack()
};
subscription.on('message', messageHandler);
}
listenForMessages();
Is it possible to reply a message once you received a data from
Publisher.
Depends on what you mean by "reply". The publisher of a message posts a message on a Pub/Sub Topic. Subscribers receive messages from a Pub/Sub Subscription. There is no two-way communications channel here. There is no Pub/Sub reply back method.
A subscriber could publish a message to a different topic that the publisher reads as a subscriber. Both sides would be publisher and a subscriber but on different topics.
Once a message is received, a subscriber could directly call an API on the publisher.
However, the intent of Publish/Subscribe is to decouple senders from receivers and not to lock them together.

Akka TcpPipeLine: how can I send message to a client/server without receiving a init.Event first?

folks!
I'm using akka 2.2.3 and developing simple tcp server application.
The work flow is:
1. client connects to server
2. server accepts connection,
3. server sends to client message "Hello!"
On page http://doc.akka.io/docs/akka/2.2.3/scala/io-tcp.html I can see how I can send response message to request. But, how can I send message before some data was received?
How can I send message to a client without receiving a init.Event first?
Code from documentation page:
class AkkaSslHandler(init: Init[WithinActorContext, String, String])
extends Actor with ActorLogging {
def receive = {
case init.Event(data) ⇒
val input = data.dropRight(1)
log.debug("akka-io Server received {} from {}", input, sender)
val response = serverResponse(input)
sender ! init.Command(response)
log.debug("akka-io Server sent: {}", response.dropRight(1))
case _: Tcp.ConnectionClosed ⇒ context.stop(self)
}
}
You use the init for creating the TcpPipelineHandler as well, and you can of course always send commands to that actor. For this you will need to pass its ActorRef to your handler actor besides the Init.

Send a push notification from a windows phone device to webservice

I have a question about the push notification service of the Windows Phone 7 device:
I can now send a push notification, using a web application to a phone, changing the data of the tile. But the problem is: when I start the app, I need to display the URI in the debugger output, and then copy-paste it inside the web application, which in turn will contact the MPNS, which is fine for an update, once to a single phone. But I want to create a webservice that can make multiple calls automatically, retrieve the URI of the application ( which changes after closing-and-opening the app, I think ) and send a push notification to it. But I haven't found an MSDN - topic that deals with this. They just use commends, saying : "to be replaced later with the URI needed." So my question is: how do I use the phone to send such a message to the webservice, respond to it, and connect to the phone again, handling such request?
and also: do I need an authenticated webservice, or is there a debug version?
This is what I have thus far :
/// <summary>
/// Setup a connection with a webservice, in order to update a shell, either a toast- or a tile shell.
/// </summary>
/// <param name="shellType">The type of shell you wish to update</param>
public void SetupShellChannel ( ShellBindType shellType )
{
//holds the push notification that is created. Since we can only have one notification channel open at any one time,
//we will need to check for existance. That is why, the channelName shouldn't be changed
HttpNotificationChannel _httpChannel = HttpNotificationChannel.Find( _channelName );
//if the _httpChannel was not found ( read: does not exist )
if ( _httpChannel == null )
{
_httpChannel = new HttpNotificationChannel( _channelName );
_httpChannel.Open( );
//because there is more than one shelltype we can open, we will use a switch to call the method we seek
BindToShell( _httpChannel, shellType );
}
//Only one push notification service is allowed per application, so we cannot send a tile notification, as well as
//a toast message notification. When we attempt this, we get a InvalidOperationException
else
{
//in this case, the _httpChannel did already exist, but the problem is, we cannot just add the eventHandlers,
//because there is the danger that it didn't exist, and we would get a null pointer exception.
//_httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>( httpChannel_ChannelUriUpdated );
//_httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>( httpChannel_ErrorOccurred );
//For testing purposes, we now display the URI to the user, and as output. Normally, we would pass this URI back to the webserver
System.Diagnostics.Debug.WriteLine( _httpChannel.ChannelUri.ToString( ) );
}
//if ( _httpChannel.ChannelUri )
//When the URI is updated, we want this to be sent to the server as well, so we know that the adress has changed,
//and don't just send data somewhere into the void. Also, when encountering an error, we want to show the user when
//an error has occured.
_httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>( HttpChannel_ChannelUriUpdated );
_httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>( HttpChannel_ErrorOccurred );
}
//here, also we would return the URI to the server, but for debugging purposes, we display them to the user.
void HttpChannel_ChannelUriUpdated( object sender, NotificationChannelUriEventArgs e )
{
Deployment.Current.Dispatcher.BeginInvoke( ( ) =>
{
System.Diagnostics.Debug.WriteLine( e.ChannelUri.ToString( ) );
MessageBox.Show( String.Format( "the URI is {0}", e.ChannelUri.ToString( ) ) );
} );
}
private void BindToShell( HttpNotificationChannel channel, ShellBindType shellType )
{
switch ( shellType )
{
case ShellBindType.BindToShellTile:
channel.BindToShellTile( );
break;
case ShellBindType.BindToShellToast:
channel.BindToShellToast( );
break;
}
}
void HttpChannel_ErrorOccurred( object sender, NotificationChannelErrorEventArgs e )
{
//getting an error would be caugth here, and then displayed to the user.
Deployment.Current.Dispatcher.BeginInvoke( ( ) =>
{
MessageBox.Show( String.Format( "A push notification {0} error occured. {1}{(2)}{3}",
e.ErrorType, e.Message, e.ErrorCode, e.ErrorAdditionalData ) );
} );
}
Ok I understand your question. What I have done is once I get teh URI from MPNS, I call a web method on a service with this as a parameter -
Subscribe(int subscriberId, Uri channelUri);
So you need to make sure you generate a subscriberId in your app to identify a user and store it in Isolated Storage. This can be a GUID.
The onus is now upon hte server to save the Subscriber to Uri mapping in persistant storage.
Also you need to provide UnSubscribe method for user to opt out of the push notification. This is one of the certification requirement for Push notifications.
Now about your second question - Yes, you would need to secure your services - you dont want to be handling with unknown requests.
What i do personally, divide it into 2 services - Publishing service and subscription service. The Publishing service will send out hte notifications while subscription will have the subscribe/unsubscribe methods.
I guess you are trying to ask that you can send push notification from Windows Phone itself or not instead using any other server side ASP/PHP like explained in Sample Applications in MSDN. Yes. You can send notifications from phone/device itself. You have to just change Send function of Sample app as given in MSDN. Reply if you have any queries.
static async Task<string> SendPushNotification(string textToSend)
{
//You can maintain a DB to query different channel URIs of devices
string subscriptionUri = "<Uri To Which You Want Send Notification>";
HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri);
sendNotificationRequest.Method = "POST";
string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>" + textToSend + "</wp:Text1>" +
"<wp:Param>/NotificationDetails.xaml?Message=" + textToSend + "</wp:Param>" +
"</wp:Toast> " +
"</wp:Notification>";
byte[] notificationMessage = Encoding.UTF8.GetBytes(toastMessage);
sendNotificationRequest.ContentLength = notificationMessage.Length;
sendNotificationRequest.ContentType = "text/xml";
sendNotificationRequest.Headers["X-WindowsPhone-Target"] = "toast";
sendNotificationRequest.Headers["X-NotificationClass"] = "2";
using (var requestStream = await Task.Factory.FromAsync<Stream>(sendNotificationRequest.BeginGetRequestStream, sendNotificationRequest.EndGetRequestStream, null))
{
requestStream.Write(notificationMessage, 0, notificationMessage.Length);
}
string notificationStatus;
using (var response = (HttpWebResponse)(await Task<WebResponse>.Factory.FromAsync(sendNotificationRequest.BeginGetResponse, sendNotificationRequest.EndGetResponse, null)))
{
//StreamReader reader = new StreamReader(response.GetResponseStream());
//result = reader.ReadToEnd();
notificationStatus = response.Headers["X-NotificationStatus"];
MessageBox.Show(notificationStatus);
}
return notificationStatus;
}