Adding and retrieving custom properties to MQ - c#

I am bit new to the whole MQ world so I am not sure about the specific technicalities.
I am struggling to send a message on a Websphere Mq with additional data; the additional data will then be used by another system for processing.
I am using the following code to do add additional properties to the mq; is this the correct way to do it?
Code in C#
IMessage sendMsg;
IMessageProducer producer;
// Create a message ---someMessage will be a xml file
sendMsg = new IMessage(someMessage);
int fileSize= size("document.pdf");
//add addtional message properties
sendMsg.SetStringProperty("MessageSize",fileSize);
// Send the message
producer.Send(sendMsg);
Also, how do i query the mq so that I get all these properties along with the actual message?
Can anyone please help; this has got me going round in circles....

Yes using SetStringProperty,SetBooleanProperty, etc,you should be able to set the properties. For getting back the property values you should use msg.GetStringProperty,msg.GetBooleanProperty etc after the message is received.In your case it has to be something like msg.GetStringProperty(MessageSize).

FYI. The MQ Knowledge Center is full of information.
Here is the page on Message. Scroll down to the 'Property methods' sections. It has all the methods to retrieve properties from a message.

Related

Can we filter messages from Amazon SQS queue by message attributes?

For now I have tried to filter the messages based on Message Attribute Name="Class". As you can see in the below code
//Specify attribute list
List<string> AttributesList = new List<string>();
AttributesList.Add("Class");
receiveMessageRequest.MessageAttributeNames = AttributesList;
receiveMessageRequest.QueueUrl = urlSQS;
receiveMessageRequest.MaxNumberOfMessages = 10;
ReceiveMessageResponse receiveMessageResponse = objClient.ReceiveMessage(receiveMessageRequest);
But the messages are not been filtered based on the provided MessageAttributeName = "class".
receiveMessageRequest.MessageAttributeNames = AttributesList;
This tells SQS which message attributes you want it to return with the message if the are present on the message. It is not a message filter. If the attributes aren't present, nothing happens.
But your confusion seems understandable -- it's not actually obvious why the API even has this functionality, though it may be a holdover from when SQS supported only smaller messages than it does today, or it may be so that you can avoid spending any time parsing information from the response that you will end up discarding. I almost always just ask for All.
Please note this regarding messaging services on AWS
SQS : No filtering support ( while picking up messages)
SNS : Supports attributes-based filtering: subscriber can set a subscription attribute (a subscription filter policy) which is applied on incoming messages and only relevant messages can be sent to the subscriber.
EventBridge: Amazon EventBridge supports declarative filtering using event patterns. With event pattern content filtering you can write complex rules that only trigger under very specific conditions. For instance, you might want a rule that will trigger only when a field of the event is within a specific numeric range, if the event comes from a specific IP address, or only if a specific field does not exist in the event JSON.
Please refer my article for a detailed difference between main messaging frameworks on AWS.
https://www.linkedin.com/pulse/mastering-art-decoupling-application-architecture-aws-amit-meena/
It depends on how the message in question gets onto the queue. If you are pushing the message via SNS then yes you can filtered messages; https://docs.aws.amazon.com/sns/latest/dg/message-filtering.html
Any other filtering mechanism doesn't exist right now.
Hope that helps!
As per the AWS SDK method, we can use following code to do the filter.
ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest("QUEUE URL" );
receiveMessageRequest.setMaxNumberOfMessages(Integer.valueOf(1));
private static AmazonSQS sqs;
List<Message> messages = sqs.receiveMessage(receiveMessageRequest.withMessageAttributeNames("Attribute Name")).getMessages();
If you want all the message then use given code
ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest("QUEUE URL" );
receiveMessageRequest.setMaxNumberOfMessages(Integer.valueOf(1));
private static AmazonSQS sqs;
List<Message> messages = sqs.receiveMessage(receiveMessageRequest.withMessageAttributeNames("All")).getMessages();

C# IBM MQ client sending my own messageId

I am sending an MQ message and getting a messageID and correlationID back in return as expected, and I can capture the response from specific message that I send using the messageID returned by the MQ server.
I put my application into a load testing tool and I saw that in some cases the messageID returned by the queue manager was the same as a previous message, and in these cases the app failed to read the next message with the same messageID.
I'm not the owner of the queue manager and the response from the admin was "created your own message id".
Can I do that? Does the messageID need to have a specific format?
This is my code :
message = strInputMsg;
queueMessage = new MQMessage();
queueMessage.WriteString(message);
queueMessage.Format = MQC.MQFMT_STRING;
queueMessage.Expiry = timeOutExpiry;
queueMessage.ReplyToQueueName = QueueNameExpiry;
queuePutMessageOptions = new MQPutMessageOptions();
queuePutMessageOptions.Options = MQC.MQRO_COPY_MSG_ID_TO_CORREL_ID;
queue.Put(queueMessage, queuePutMessageOptions);
bytReturn = queueMessage.MessageId;
So can I set the MessageID property to my own message ID value before I send the message, like the below?
queueMessage.MessageId = myOwnMessageId
Yes, it's possible that the code sets the message ID explicitly but your code appears to not reuse the MQMD structure which is how that normally happens. It is more likely based on your description and code provided that the load testing tool is replaying the same messages multiple times and preserving the Message ID while doing so.
If MQ is allowed to set the Message ID it guarantees this to be unique within the queue manager for IDs that it generates. It does not guarantee a GUID across many queue managers but does attempt to ensure no collisions by including 12 characters of the QMgr name in the Message ID. So although we have no information as to which load testing tool is being used and if it employs message replay, that possibility seems much more likely than that MQ has a bug that duplicates message IDs during execution of the .Net MQMessage() class constructor.
Please see:
MQMessage.NET class which says "Creates an MQMessage object with default message descriptor information..." Of course, the default MQMD causes the QMgr to generate the Message ID.
MQMD - MsgId (MQBYTE24) which explains in a note at the bottom how MsgID is made to be unique and in the body how it can be controlled by the application putting the messages.

Mimekit/mailkit download message body?

I have been making my own mail client recently and added a receive option, I used mimekit and mailkit as plugins and was able to download most of my mails with a reader that is supposed to show content(right now it shows subject, to, from, date)
The way I downloaded the subject, to, ... is msg.envelope.subject, msg.envelope.to
But I cannot download the body like this :( when doing either msg.body, msg.textbody, msg.bodyparts, ... they all result in NOTHING, the place where it should be is just empty, I can't get it downloaded :(
Can anyone help me?
There are 2 ways to get the message body:
1. Download the Whole Message
This method is probably the easiest way.
To do this, all you need to do is call:
var message = folder.GetMessage (uid);
or
var message = folder.GetMessage (index);
I would recommend always using the UniqueId of the message. Since you are already using the Fetch method, all you need to do to make sure that you have the UniqueId of the message is to include the MessageSummaryItems.UniqueId in your fetch request:
var messages = folder.Fetch (0, -1, MessageSummaryItems.UniqueId |
MessageSummaryItems.Envelope | ...);
Once you have the message, you can do whatever you want with it.
For rendering the message, I would recommend taking a look at the MessageReader sample included in the MimeKit GitHub repository.
It will show you how to properly render a MimeMessage.
2. Download Only What You Need
This method is a bit harder but can be more efficient as far as network bandwidth usage goes.
The first thing you need to do is to make sure to include the MessageSummaryItems.BodyStructure bit flag in the Fetch method. For example:
var messages = folder.Fetch (0, -1, MessageSummaryItems.Envelope |
MessageSummaryItems.BodyStructure);
(You'll probably want other fields, but that's just an example to show you how to bitwise-or flags together to request multiple message summary items).
By requesting the BodyStructure of the messages, you'll be able to make use of the msg.Body property.
Each msg.Body will be a BodyPart object which is an abstract class. The 2 main subclasses are BodyPartMultipart and BodyPartBasic. You can use the as cast or the is keyword to figure out which one it is:
var multipart = msg.Body as BodyPartMultipart;
if (multipart != null) {
// the top-level body part is a multi-part
} else {
// the body is a basic singleton part
}
This is how you would iterate over the subparts of a BodyPartMultipart:
foreach (var part in multipart.BodyParts) {
// each part will either be a BodyPartMultipart
// or a BodyPartBasic, just like before...
}
There are also 2 subclasses of BodyPartBasic which are: BodyPartText and BodyPartMessage. A BodyPartText is a textual-based MIME part (meaning it has a MIME-type of text/*) whereas a BodyPartMessage is an embedded message (and will have a MIME-type of message/rfc822).
Since MIME is recursive, you'll need to implement a recursive function to walk the MIME tree structure to find whatever MIME part you are looking for.
For your convenience, the TextBody and HtmlBody properties on the IMessageSummary interface will locate and return the text/plain and text/html body parts, respectively.
It should be noted, however, that these properties only work in cases where the structure of the message follows the standard convention (notice I said convention, there is no formal standard dictating the location of the message text within a MIME hierarchy).
It should also be noted that if your mail client will be rendering the HTML body, the HTML body part may be part of a group of related MIME parts (i.e. a child of a multipart/related), but the HtmlBody property will not be able to return that and so implementing your own recursive logic will be a better option.
For an example of how to do this, check out the ImapClientDemo sample in the MailKit GitHub repository. The logic currently resides in the MainWindow.cs code.

Using Commands in my IRC Client

im currently trying to make a simple IRC Gui Client. Im using the SmartIrc4net as a base, as it seems to be the most supportive out of all of them, found here: http://www.codeproject.com/Articles/8323/SmartIrc4net-the-C-IRC-library
Now What I am having problem with is the action commands. For example to make yourself an oper, you would type
/oper admin password or to changehost, would be /sethost mynewhost
My problem is that when I pass that value through a TextBox, instead of making me admin, or changing my host. My input just gets displayed as text in the chat.
Here is my code:
string[] serverlist;
serverlist = new string[] { "mydomain.com" };
int port = 6667;
string channel = "#MyChannel#";
try
{
irc.Connect(serverlist, port);
irc.Login("SmartIRC", "SmartIrc4net Test Bot");
irc.RfcJoin(channel);
irc.SendMessage(SendType.Message, channel, "/oper admin mypass");
irc.SendMessage(SendType.Action, channel, "/sethost mynewhost");
irc.Listen();
But when I pass those values, all it does is just display what I typed in the chat, without actually making me oper, or changing my sethost.
Is there anyway that I could actually make it pass commands through to the IRC server, instead of just displaying the raw text on the chat?
Any help would be appreciated, Thanks
This is because you are explicitly sending a message. IRC itself has no notion of /commands, this is done all in the client. What you are doing is to just send a message with a specific text that happens to start with /. I.e. what the server receives is
PRIVMSG #channel :/oper admin mypass
instead of
OPER admin mypass
You just need to figure out a way of sending raw IRC commands to the server. The page you linked to doesn't offer much documentation on that part, though. But judging from the layers this should be in either Layer 2 or Layer 1.
There is a lot of more options then SendMessages.
You have example irc.RfcOper(username, pasword) for Oper.
If you want to send raw data command for things it does not support on the fly example sethost you can just send a WriteLine directly.
irc.WriteLine("sethost mynewhost")
Open the IrcCommands.cs to see a list of commands and Rfc2812.cs to see how they are transfered.
I however recommend you to read or at least peek at Rfc2812 standard that you can find here https://www.rfc-editor.org/rfc/rfc2812

MS CRM 4.0 CrmService - Close a Task

I'm trying to work out how to use the CrmService to close a Task in MS CRM 4.0
I've tried to use the SetStateTaskRequest to set a Task's state and status to TaskState.Completed and 5. I also tried TaskState.Completed and -1, but no dice there either.
Either way, I only receive the ever-helpful "Server was unable to process request" exception on the CrmService.Execute attempt.
I can create and update Tasks as freely as I please. But I can't seem to set them to completed. It's frustrating.
I noticed that I can only set the state of a Task to Completed in CRM through the Close Task action. I was wondering if there is a separate CrmService call that I need to make to perform the Close Task action, rather than going through the CrmService.Execute method.
Oh: I'm logging into the CrmService with full permissions. So I can't see that it would be a permissions issue on the task item.
I can't think what else could be causing this issue. Any advice or even just a point in the right direction would be very much appreciated.
FIRST EDIT:
Thanks to grega g's answer for getting me to check the Detail field of the exception.
I now have a more detailed exception message. In XML Form:
<error>
<code>0x80040203</code>
<description>Invalid format of input XML for request SetStateTask: required field 'EntityId' is missing.</description>
<type>Platform</type>
</error>
Which is bizarre - consider my code (almost identical to greg g's:
SetStateTaskRequest request = new SetStateTaskRequest();
request.EntityID = gTaskId;
request.TaskState = TaskState.Completed;
// ETaskStatusCode is an enumeration of the status codes taken from the StringMap in CRM.
//
// ETaskStatusCode.Completed = 5 - I can confirm this is the accurate status value for a Closed Task.
//
// Also, I have attempted this code with -1, which the documentation claims should cause the status
// to automatically be set to the default status for the supplied state. No change there.
request.TaskStatus = (int)ETaskStatusCode.Completed;
SetStateTaskResponse response = CRMManager.CrmService.Execute(request) as SetStateTaskResponse;
Also, just to confirm that I have the right status code (and also share something I've found very useful when dealing with MS CRM), here's the SQL I use to determine the values for entity statuses.
SELECT
MSE.ObjectTypeCode,
MSE.PhysicalName,
SM.AttributeName,
SM.Value,
SM.AttributeValue
FROM MetadataSchema.Entity MSE
INNER JOIN StringMap SM on MSE.ObjectTypeCode = SM.ObjectTypeCode
ORDER BY MSE.PhysicalName, SM.AttributeName, SM.AttributeValue
I can confirm from the MS CRM web interface that the Status value that is associated with a Completed task is also named Completed. I can confirm from the SQL above that the value of this status, for a Task, is 5 - this is the value passed in from my Enum.
I can also confirm that gTaskId is being set to a valid Guid that references a Task that actually does exist, and is open at the time the close is attempted.
Curiouser and curiouser. Any thoughts?
Use SetStateTaskRequest class.
SetStateTaskRequest task2Close = new SetStateTaskRequest();
task2Close.EntityId = <taskGuid>
task2Close.TaskState = TaskState.Completed;
task2Close.TaskStatus = <some value>
try
{
SetStateTaskResponse r = (SetStateTaskResponse) crmSvc.Execute(task2Close);
}
catch (SoapException e)
{
//Use e.Details for more info than "server was unable ..."
}
This code should work (or let you see why error occurs)
Are you sure that when you are trying to close task you're passing status value which is valid for Completed state? Different status codes are only valid with their corresponding state codes. Can you add your source code and a portion of your state entity customization?
Found it!
Okay - reviewing my code above and the error message closely, my CrmService contained the property EntityID - but the exception was that the property EntityId was missing.
Somehow, my CrmService had its EntityId property renamed to EntityID.
Renaming the property fixed the problem. I still have no idea how that happened in the first place.
To be safe, I'll regenerate a new Service proxy to make sure that my properties are correctly named.
Looking through the code, it seems that someone did a find-and-replace on 'Id' and turned it into 'ID' - which incidentally is the naming convention in my workplace for Property fields that represent primary keys.
Thanks again to grega g for pointing out that the Detail property had the extra information I needed.

Categories

Resources