I have a XMS publish app, that is working, but it is including JMS headers as part of the message. My subscribe app is actually a python app, and I was wondering if it is possible to remove the JMS headers from the XMS app. I know it is possible in JMS, but is it possible in C# / XMS.
My C# code is fairly simple (with some of the details left out -
// Get an instance of factory.
factoryFactory = XMSFactoryFactory.GetInstance(XMSC.CT_WMQ);
// Create WMQ Connection Factory.
cf = factoryFactory.CreateConnectionFactory();
// Set the properties
cf.SetStringProperty(XMSC.WMQ_HOST_NAME, conn.host);
...
// Create connection.
connectionWMQ = cf.CreateConnection();
// Create session
sessionWMQ = connectionWMQ.CreateSession(false, AcknowledgeMode.AutoAcknowledge);
// Create destination
destination = sessionWMQ.CreateTopic(conn.topic_name);
// Create producer
producer = sessionWMQ.CreateProducer(destination);
// Start the connection to receive messages.
connectionWMQ.Start();
// Create a text message and send it.
textMessage = sessionWMQ.CreateTextMessage();
textMessage.Text = xmsJson.toJsonString();
producer.Send(textMessage);
In MQ /JMS I can use setTargetClient to remove the JMS headers -
private void setToNoJMSHeaders(Destination destination) {
try {
MQDestination mqDestination = (MQDestination) destination;
mqDestination.setTargetClient(WMQConstants.WMQ_CLIENT_NONJMS_MQ);
} catch (JMSException jmsex) {
logger.warning("Unable to set target destination to non JMS");
}
}
I was wondering if I can do the same to the topic destination in XMS
// Create destination
destination = sessionWMQ.CreateTopic(conn.topic_name);
// Configure the destination to Non-JMS
... ???
Yes, you should be able to do this
Try
// Create destination
destination = sessionWMQ.CreateTopic(conn.topic_name);
destination.SetIntProperty(XMSC.WMQ_TARGET_CLIENT, XMSC.WMQ_TARGET_DEST_MQ);
See: https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.ref.dev.doc/prx_wmq_target_client.htm
Related
I have a C# client for fetching IBM mq messages. I am trying to read byte messages from the queue. But I receive the following error.
IBM.XMS.MessageEOFException: CWSMQ0136I: An attempt was made to read beyond the end of the message.
An attempt was made to read beyond the end of the message. This may be a normal condition if an application has been coded to read variable length data using the JMS 1.0.2 specification.
If necessary, recode the application to use the new getBodyLength method.
at IBM.XMS.Client.Impl.XmsBytesMessageImpl.ReadUTF()
I tried with the following code in C#,
var msg = (IBytesMessage)message;
var result = msg.ReadUTF();
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(result);
The error suggests I use getBodyLength()
in java I have seen
byte[] uploadPayload = null;
BytesMessage bytesMessage = (BytesMessage) receivedMessage;
uploadPayload = new byte[(int) bytesMessage.getBodyLength()];
bytesMessage.readBytes(uploadPayload);
But how do I do that in C#. I see that it's the GetBodyLength is not available ?
Hope this sample helps you. There is BodyLength property on the message that can be used to determine the body length. I have used the same here in the example below. Do you really need to allocate the byte array? If you know the format of the incoming bytes message, then you could simply use the different Read methods of IBytesMessage class to read the data. For example use ReadInt to read the Integer data, then if you have a byte, then call ReadByte etc.
void ReceiveMessages()
{
XMSFactoryFactory factoryFactory;
IConnectionFactory cf;
IConnection connectionWMQ;
ISession sessionWMQ;
IDestination destination;
IMessageConsumer consumer;
IBytesMessage bytesMessage;
// Get an instance of factory.
factoryFactory = XMSFactoryFactory.GetInstance(XMSC.CT_WMQ);
// Create WMQ Connection Factory.
cf = factoryFactory.CreateConnectionFactory();
Console.WriteLine("Connection Factory created.");
// Set the properties
cf.SetStringProperty(XMSC.WMQ_HOST_NAME, "localhost");
cf.SetIntProperty(XMSC.WMQ_PORT, 1414);
cf.SetStringProperty(XMSC.WMQ_CHANNEL, "SVR_CHN");
cf.SetIntProperty(XMSC.WMQ_CONNECTION_MODE, XMSC.WMQ_CM_CLIENT);
cf.SetStringProperty(XMSC.WMQ_QUEUE_MANAGER, "MYQM");
cf.SetStringProperty(XMSC.USERID, "myuserId");
cf.SetStringProperty(XMSC.PASSWORD, "mypassw0rd");
// Create connection.
connectionWMQ = cf.CreateConnection();
Console.WriteLine("Connection created.");
// Create session
sessionWMQ = connectionWMQ.CreateSession(false, AcknowledgeMode.AutoAcknowledge);
Console.WriteLine("Session created.");
// Create destination
destination = sessionWMQ.CreateQueue("INBQ");
Console.WriteLine("Destination created.");
IMessageProducer prod = sessionWMQ.CreateProducer(destination);
IBytesMessage sendMsg = sessionWMQ.CreateBytesMessage();
sendMsg.WriteBytes(Encoding.ASCII.GetBytes("Hello world from WMQ"));
prod.Send(sendMsg);
// Create consumer
consumer = sessionWMQ.CreateConsumer(destination);
Console.WriteLine("Message Consumer created. Starting the connection now.");
// Start the connection to receive messages.
connectionWMQ.Start();
// Wait for 30 seconds for messages. Exit if no message by then
bytesMessage = (IBytesMessage)consumer.Receive(30000);
if (bytesMessage != null)
{
Console.WriteLine("Message received.");
byte[] uploadPayload = null;
uploadPayload = new byte[(int)bytesMessage.BodyLength];
bytesMessage.ReadBytes(uploadPayload);
Console.WriteLine(bytesMessage.BodyLength + "\n");
}
else
Console.WriteLine("Wait timed out.");
// Cleanup
consumer.Close();
destination.Dispose();
sessionWMQ.Dispose();
connectionWMQ.Close();
}
I am trying to retrieve the MAC address of a device that connects to the network. My goal is to perform a WALK and then search the results by the port number that triggered the event.
I first grab the port information via a GetRequestMessage (Success). Then I TRY to perform a walk to get the MAC Address table. I do not get any errors or exceptions, but I also do not get any results.
Where am I going wrong?
// Capture IPAddress
string ipAddress = e.Sender.Address.ToString();
// Capture the port
string port = e.Message.Scope.Pdu.Variables[0].Data.ToString();
// Setup Authentication with password from App.Config
var auth = new SHA1AuthenticationProvider(new OctetString(_Password));
// Setup Privacy with Phrase from App.Config
var priv = new DESPrivacyProvider(new OctetString(_Phrase), auth);
// Setup username from App.Config
OctetString userName = new OctetString(_Username);
// Create IPEndPoint
IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse(ipAddress), 161);
// Create discovery
Discovery discovery = Messenger.GetNextDiscovery(SnmpType.GetRequestPdu);
// Create report
ReportMessage report = discovery.GetResponse(60000, iPEndPoint);
// Setup OID variables to get port information
List<Variable> variables = new List<Variable>
{
// Port Description
new Variable(new ObjectIdentifier($"1.3.6.1.2.1.31.1.1.1.18.{ port }")),
// Port PVID
new Variable(new ObjectIdentifier($"1.3.6.1.2.1.17.7.1.4.5.1.1.{ port }")),
// Voice VLAN
new Variable(new ObjectIdentifier($"1.3.6.1.4.1.674.10895.5000.2.6132.1.1.1.2.13.1.28.{ port }")),
};
// Create SNMP request
GetRequestMessage request = new GetRequestMessage(VersionCode.V3, Messenger.NextMessageId, Messenger.NextRequestId, userName, variables, priv, Messenger.MaxMessageSize, report);
// Send request and get reply
ISnmpMessage reply = request.GetResponse(60000, iPEndPoint);
// Request failed
if (reply.Pdu().ErrorStatus.ToInt32() != 0) // != ErrorCode.NoError
{
throw ErrorException.Create(
"error in response",
IPAddress.Parse(ipAddress),
reply);
}
// Store reply information
string Port_Description = reply.Scope.Pdu.Variables[0].Data.ToString(),
Port_Pvid = reply.Scope.Pdu.Variables[1].Data.ToString(),
Port_VLAN = reply.Scope.Pdu.Variables[2].Data.ToString();
// Create BulkWalk Discovery
// TODO: Do I need to do this or can I reuse the original discovery??
Discovery BULKWALK_discovery = Messenger.GetNextDiscovery(SnmpType.GetRequestPdu);
// Create BulkWalk report
// TODO: Do I need to do this or can I reuse the original report??
ReportMessage BULKWALK_report = BULKWALK_discovery.GetResponse(60000, iPEndPoint);
// Variable to store the results of the WALK
var BULKWALK_results = new List<Variable>();
// Perform the walk and return the count of results. Community name from App.Config
var BULKWALK_results_count = Messenger.BulkWalk(VersionCode.V3, iPEndPoint, new OctetString(_CommunityName), OctetString.Empty, new ObjectIdentifier($"1.3.6.1.2.1.17.7.1.2.2.1.2"), BULKWALK_results, 60000, 10, WalkMode.WithinSubtree, priv, BULKWALK_report);
Debugger.Break();
EDIT
Also, I am using this resource as guidance.
So I found the issue, which was two fold.
The first was when instantiating the Discovery for the BulkWalk, it needs to be as follows:
Discovery discovery = Messenger.GetNextDiscovery(SnmpType.GetBulkRequestPdu);
The key part being getting the SnmpType correct (my code above is SnmpType.GetRequestPdu and not SnmpType.GetBulkRequestPdu). The tutorial does not mention that the SnmpType is different.
Second, when passing parameters into the Messenger.BulkWalk() method I was passing in the community name and not the user name. The source code remarks for the BulkWalk method does say community name but it should be the user.
I did as Lex Li suggested and complied/ran the snmpwalk sample to verify that there were no issues. After that was a success, I went back and reviewed the source code for that sample and found the two issues.
For each site in a single port, I need to connect to different server hosts.
While connecting for the first time, no issues. But while trying to register (RegisterDestinationConfiguration) with another connection pool name, it throws an exception. Destination configuration is already initialized for ConnectionPoolName1 so I can't register for ConnectionPoolName2.
function() {
if (SAPDestination == null) {
SAPDestination = SAPConnection(ApplicationSite);
RfcSessionManager.BeginContext(SAPDestination);
}
rfcTravelfunc = SAPDestination.Repository.CreateFunction("FunctionName");
}
private RfcDestination SAPConnection(SPSite ApplicationSite) {
RfcDestination SAPConnect = null;
try {
DestinationConfig objConfig = new DestinationConfig();
SAPConnect = objConfig.TryGetDestination(ConnectionPoolName); // If connection doesnt exist with this connection pool name returns null
if (SAPConnect == null) {
DestinationConfig configObj = new DestinationConfig();
DestinationConfig.ApplicationSite = ApplicationSite;
RfcDestinationManager.RegisterDestinationConfiguration(configObj); // Throws exception when trying to register for new connection pool name
SAPConnect = RfcDestinationManager.GetDestination(ConnectionPoolName);
}
}
catch(Exception ex) {
}
return SAPConnect;
}
RfcDestinationManager.RegisterDestinationConfiguration() is a global static method. You can only register once. It should be set in a static context (like a static class constructor) or you can use RfcDestinationManager.IsDestinationConfigurationRegistered() to check if already registered. The exception is thrown to prevent incorrect usage.
The registration object needs to implement SAP.Middleware.Connector.IDestinationConfiguration.
It has a method RfcConfigParameters GetParameters(string destinationName); This should return connection parameters for the requested destination.
You should use this way only if you need to look up the connection parameters from an external store. The simpler method is to store the connection parameters in the app.config/web.config (can store multiple) and not use RfcDestinationManager.RegisterDestinationConfiguration().
Taken from the SAP NCo Tutorial Code StepByStepClient.cs
The .Net Connector 3.0 introduces a new destination-oriented concept. Applications work with destination instances, which are configured per default in the application configuration file (app.config) or which can alternatively be defined by explicitly registering an IDestinationConfiguration object. A destination identifies the backend to which connections can be opened.
I'm writing an application that uses WebSphere MQ for messaging. For my unittests (flowtests), I want to verify that I have put the right messages on the response queue.
I'm trying to figure out how to do this. My main obstacle is that I think it might be scary to clear a queue before I run my unittest, because the same queue might be used by another application.
I thought a decent workaround would be to create a new queue manager and queue for my unittest and delete it after using it.
So my question is: Is it possible to create a queue manager and queue using C#?
For future reference and future people who want to create queues. I figured out how to create and delete IBM MQ queues (not queuemanagers) with PCF messaging. It is not very straightforward, but it can be done.
We have implemented it in a library and are using it to create and delete queues before and after integration tests respectivally. The most important part of code in this library is shown in the code sample below. Just add a reference to amqmdnet.dll and below code will create a queue and delete it.
string queueManagerName = "QM_LOCAL";
string queueName = "DeleteMeQueue";
Hashtable options = new Hashtable();
// This is a connection to a local server. For a remote server use 'TRANSPORT_MQSERIES_CLIENT', 'TRANSPORT_MQSERIES_XACLIENT' or 'TRANSPORT_MQSERIES_MANAGED'
options.Add(IBM.WMQ.MQC.TRANSPORT_PROPERTY, "TRANSPORT_MQSERIES_BINDINGS");
// For 'TRANSPORT_MQSERIES_CLIENT', 'TRANSPORT_MQSERIES_XACLIENT' or 'TRANSPORT_MQSERIES_MANAGED' uncomment the below
// string hostName = "RemoteServerName";
// string channelName = "SYSTEM.ADMIN.SVRCONN";
// int portNumber = 1414;
// options.Add(IBM.WMQ.MQC.HOST_NAME_PROPERTY, hostName);
// options.Add(IBM.WMQ.MQC.CHANNEL_PROPERTY, channelName);
// options.Add(IBM.WMQ.MQC.PORT_PROPERTY, portNumber);
// options.Add(IBM.WMQ.MQC.CONNECT_OPTIONS_PROPERTY, IBM.WMQ.MQC.MQC.MQCNO_STANDARD_BINDING);
IBM.WMQ.MQQueueManager queueManager = null;
IBM.WMQ.PCF.PCFMessageAgent agent = null;
try
{
// Initialize a connection to the (remote) queuemanager and a PCF message agent.
queueManager = new IBM.WMQ.MQQueueManager(queueManagerName, options);
agent = new IBM.WMQ.PCF.PCFMessageAgent(queueManager);
// Create queue
IBM.WMQ.PCF.PCFMessage createRequest = new IBM.WMQ.PCF.PCFMessage(IBM.WMQ.PCF.CMQCFC.MQCMD_CREATE_Q);
createRequest.AddParameter(IBM.WMQ.MQC.MQCA_Q_NAME, queueName);
createRequest.AddParameter(IBM.WMQ.MQC.MQIA_Q_TYPE, IBM.WMQ.MQC.MQQT_LOCAL);
createRequest.AddParameter(IBM.WMQ.MQC.MQIA_DEF_PERSISTENCE, IBM.WMQ.MQC.MQPER_PERSISTENT);
createRequest.AddParameter(IBM.WMQ.MQC.MQCA_Q_DESC, "Created by " + Environment.UserName + " on " + DateTime.UtcNow.ToString("o"));
IBM.WMQ.PCF.PCFMessage[] createResponses = agent.Send(createRequest);
// Delete queue
IBM.WMQ.PCF.PCFMessage deleteRequest = new IBM.WMQ.PCF.PCFMessage(IBM.WMQ.PCF.CMQCFC.MQCMD_DELETE_Q);
deleteRequest.AddParameter(IBM.WMQ.MQC.MQCA_Q_NAME, queueName);
IBM.WMQ.PCF.PCFMessage[] deleteResponses = agent.Send(deleteRequest);
}
finally
{
// Disconnect the agent and queuemanager.
if (agent != null) agent.Disconnect();
if (queueManager != null && queueManager.IsConnected) queueManager.Disconnect();
}
Creation of queue manager and queues are administrative jobs. Creation of queue manager can not be done using an user defined application. You have to use the command crtmqm <qmname> provided by MQ to create queue manager.
I would suggest you ask your queue manager administrator to create dedicated queue for you. Only your unit test use this queue and no other user is allowed to put/get messages to this queue.
I have win service that work with MQ.
But i want that it works using ssl channel and database with public/private keys(for that)
May you explain me how to do it.
P.S. I'm not very good at MQ
now i connect to MQ using this code
MQEnvironment.Hostname = ConfigurationManager.AppSettings["HostnameIN"];
MQEnvironment.Channel = ConfigurationManager.AppSettings["ChannelIN"];
MQEnvironment.Port = int.Parse(ConfigurationManager.AppSettings["PortIN"]);
Environment.SetEnvironmentVariable("MQCCSID", ConfigurationManager.AppSettings["MQCCSID"]);
var mqQueueManagerName = ConfigurationManager.AppSettings["QueueManagerNameIN"];
var mqQueueName = ConfigurationManager.AppSettings["QueueNameIN"];
const int openOptions = MQC.MQOO_BROWSE | MQC.MQOO_INPUT_AS_Q_DEF;
var qMgr = new MQQueueManager(mqQueueManagerName);
var getOptions = new MQGetMessageOptions();
and get all messages using this
using (var mqQueue = qMgr.AccessQueue(mqQueueName, openOptions))
{
try
{
//while (mqQueue.CurrentDepth>0)
while (true)
{
var message = new MQMessage();
//message.Version = 2;
getOptions.Options = MQC.MQGMO_WAIT | MQC.MQGMO_BROWSE_NEXT;
mqQueue.Get(message, getOptions);
mqMessages.Add(message);
}
}
In order to set up MQ to use SSL on the channel you're using, you don't need to make any application changes at all - you simply need to configure the channel you're using on the queue manager to require SSL. The libraries within the client, JVM, and the queue manager will handle establishing that secure connection for you. So in theory all you need to do is make the MQSC/MQ Explorer changes which will configure SSL on the channel.
Recommend you read the following page in the IBM knowledge center. It provides a number of scenarios for various methods of connecting a client securely to the queue manager:
http://www-01.ibm.com/support/knowledgecenter/SSFKSJ_8.0.0/com.ibm.mq.sce.doc/q014220_.htm