Following this example of sending messages to queue let's look at the part of setting connection factory properties
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, HOST);
cf.setIntProperty(WMQConstants.WMQ_PORT, PORT);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, CHANNEL);
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, QMGR);
cf.setStringProperty(WMQConstants.WMQ_APPLICATIONNAME, "JmsPutGet (JMS)");
cf.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
cf.setStringProperty(WMQConstants.USERID, APP_USER);
cf.setStringProperty(WMQConstants.PASSWORD, APP_PASSWORD);
When the line cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, QMGR); is removed then nothing changes: the client still can successfully send and receive messages.
What is the purpose of setting WMQConstants.WMQ_QUEUE_MANAGER property here?
IBM MQ server is a container based on this Dockefile:
FROM ibmcom/mq:9.2.2.0-r1
ENV LICENSE=accept
ENV MQ_DEV=true
ENV MQ_APP_PASSWORD=app-password
ENV MQ_ADMIN_PASSWORD=admin-pwd
ENV MQ_QMGR_NAME=KUPOL_DEV_QM
Additionally:
In the same example we see the line
destination = context.createQueue("queue:///" + QUEUE_NAME)
and it does not break the app if the prefix "queue:///" is removed, leaving the line as
destination = context.createQueue(QUEUE_NAME).
And I see similar things in multiple examples for IBM MQ across the web.
What is going on with this code? Is it blind copy-pasting or is there an intention?
If you leave queue manager unset or specify a value that is prefixed with a * you can connect to any queue manager name listening on the host and port you specify.
If you specify a queue manager name that is not prefixed with a * then it must match the name of the queue manager listening on the host and port.
You can also use a CCDT to hold the the connection details, in this case (in addition to the above points) the queue manager name you specify is used to look up the connection details in the CCDT. If it is prefixed with a * it will look up the name without the * in the CCDT.
In addition to specifying queue names with the queue:/// prefix, you can also specify topics with the prefix topic:///, my guess is createQueue defaults to assume you are specifying a queue name.
Your sample code appears to be JMS. IBM MQ supports a couple of addressing models for queues.
All three of these mean the same thing:
context.createQueue("MY.QUEUE");
context.createQueue("queue://MY.QUEUE");
context.createQueue("queue:///MY.QUEUE");
However, with the triple-slash you can also fully-qualify REMOTE queues
context.createQueue("queue://QMGRB/MY.OTHER.QUEUE");
This tells IBM MQ to send the message to the QMgr and have it deliver the message to the Remote Queue 'MY.OTHER.QUEUE' on 'QMGRB'.
Note: IBM MQ also supports destination options, so you can modify persistence, priority, character encoding, targetClient, etc. This is useful so you can externalize the configuration and change the message pattern without having to change the code!
ref: https://www.ibm.com/docs/en/ibm-mq/9.0?topic=applications-creating-destinations-in-jms-application
Related
Now that I managed to get the address to auto delete (based on this question) I cannot figure out what is preventing my producer from recreating the address.
When there's no producer pushing data to a specific address I need it to auto delete. Right now when the last consumer disconnects the address gets deleted, and the producer will not recreate the address.
Below is the producer code. It's just a simple Java function that reads a value from an XML message and uses that as the address name. It's basically sorting the messages into different topics based on the name in the XML message itself:
/////////////////////////////////////
//Constructor to establish connection
/////////////////////////////////////
public ActiveMQ(String amq_url) throws JMSException, NamingException{
jndi_env = new Hashtable<String, Object>();
jndi_env.put(InitialContext.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory");
jndi_env.put(InitialContext.PROVIDER_URL, amq_url);
ic = new InitialContext(jndi_env);
connectionFactory = (ConnectionFactory) ic.lookup("ConnectionFactory");
this.connection = (ActiveMQConnection) connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
}
/////////////////////////////////////
//Switch address to publish to
/////////////////////////////////////
public void useAddress(String addressName) throws JMSException, NamingException{
if (producer != null) producer.close();
this.address= (Destination) ic.lookup("dynamicTopics/"+addressName);
this.producer = session.createProducer(address);
}
The only way to get this line of code to make the address again is to restart the producer program or the Artemis server. I've tried having the logic completely close everything and re-open it (session, connection, etc) and that didn't fix it. Is there a setting that I'm missing here? I can't find anything in the documentation that covers why the address is not being recreated by the producer. Any help would be appreciated!
broker.xml (same as from other question):
<address-setting match="#">
<dead-letter-address>DLQ</dead-letter-address>
<expiry-address></expiry-address>
<redelivery-delay>0</redelivery-delay>
<expiry-delay>10</expiry-delay>
<max-size-bytes>-1</max-size-bytes>
<max-size-messages>-1</max-size-messages>
<message-counter-history-day-limit>10</message-counter-history-day-limit>
<address-full-policy>PAGE</address-full-policy>
<auto-create-queues>true</auto-create-queues>
<auto-create-addresses>true</auto-create-addresses>
<auto-delete-queues>true</auto-delete-queues>
<auto-delete-addresses>true</auto-delete-addresses>
<auto-delete-created-queues>true</auto-delete-created-queues>
<auto-delete-queues-message-count>-1</auto-delete-queues-message-count>
</address-setting>
EDIT:
So there really isn't an easy way to do this with the settings in Artemis alone. I think the best solution for what I need, based on Justin's answer below, is to turn off auto-delete settings and do this through java programming, using something similar to my other question where I read the available addresses, and delete the ones that arent relevant anymore.
For now, setting a long delay using <auto-delete-address-delay> will suit my current needs temporarily.
I believe this is an edge case that isn't covered currently.
When you create a named producer (e.g. using javax.jms.Session.createProducer(Destination)) or send a message to a destination with an anonymous producer (e.g. using javax.jms.MessageProducer.send(Destination, Message)) the client checks to see if the destination exists. If the destination doesn't exist then the client will create it if the proper auto-create settings are true. The client then caches this information so that it doesn't have to keep checking every time it sends a message to the destination in question as checking every time would be prohibitively time consuming. Of course, not checking means the destination can go away and the client wouldn't know.
When you restart the application and the producer is created again the address gets re-created.
It's not clear why you need the address to be deleted when no producer is actively pushing messages to it. In general I would say that your use-case doesn't fit well with auto-delete. Auto-delete was really designed for use-cases involving single-use or ephemeral destinations. The address definition in memory consumes very few resources so generally speaking it's fine to just leave it. I recommend you disable auto-delete (as it is by default) to mitigate this issue for now.
I am trying to connect to IBM MQ using JMS and client channel definition table (CCDT). I was able to connect successfully to the QueueManager when i specify the MQ properties individually.
But when i try to use CCDT file i get the below exception.
As client channel definition table (CCDT) is used to determine the channel definitions used by client applications to connect to the queue manager i didnt set QueueManager Name.
ERROR> com.ssc.ach.mq.JMSMQReceiver[main]: errorMQJMS2005: failed to create MQQueueManager for ''
javax.jms.JMSException: MQJMS2005: failed to create MQQueueManager for ''
at com.ibm.mq.jms.services.ConfigEnvironment.newException(ConfigEnvironment.java:586)
at com.ibm.mq.jms.MQConnection.createQM(MQConnection.java:2110)
at com.ibm.mq.jms.MQConnection.createQMNonXA(MQConnection.java:1532)
at com.ibm.mq.jms.MQQueueConnection.<init>(MQQueueConnection.java:150)
at com.ibm.mq.jms.MQQueueConnectionFactory.createQueueConnection(MQQueueConnectionFactory.java:174)
at com.ibm.mq.jms.MQQueueConnectionFactory.createConnection(MQQueueConnectionFactory.java:1066)
Iam using the .setCCDTURL(ccdt); method to set the CCDT URL.
private MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
mqQueueConnectionFactory.setCCDTURL(ccdt);
queueConnection = mqQueueConnectionFactory.createConnection(username, pwd);
When i try to connect using below configuration instead of the CCDT file it connects to the MQ.
mqQueueConnectionFactory.setHostName(host);
mqQueueConnectionFactory.setChannel(channel);
mqQueueConnectionFactory.setPort(port);
mqQueueConnectionFactory.setQueueManager(qManager);
mqQueueConnectionFactory.setTransportType(1);
Do i need to set setQueueManager as well along with the CCDT file , as the exception says failed to create MQQueueManager for ''
The CCDT is not meant to be read in a text editor, it is a binary formatted file. One of the parameters in the CCDT for each CLNTCONN channel is QMNAME. Knowing what QMNAME is set to and how many CLNTCONN channels you have defined in the CCDT and what you want to accomplish will help figure out what value if any should be specified for with setQueueManager.
If there is only one CLNTCONN channel then you could specify the following and it will connect using the single channel no matter what the QMNAME property is set to:
setQueueManager("*");
If there is more than one CLNTCONN channel in the file each with a different QMNAME specified, assuming the name matches the actual queue manager name listening on the host and port associated with the channel you would pass the queue manager name:
setQueueManager("QMGRNAME");
If there is more than one CLNTCONN channels in the file each with the same QMNAME specified where this name is not meant to reflect a actual queue manager name listening on the host and port associated with each channel, this is known as a queue manager group, this would be intended where you want the client to connect to any number of different hosts and ports and you do not need to know which queue manager you are connecting to, in this case you would pass the queue manager group name prefixed with a *:
setQueueManager("*QMGRGROUPNAME");
Another variation of the above is if there is more than one CLNTCONN channels in the file each with an all blank (spaces) or NULL QMNAME specified, this is known as a queue manager group, this would be intended where you want the client to connect to any number of different hosts and ports and you do not need to know which queue manager you are connecting to, in this case you would pass the queue manager name as either a single space or nothing at all ``:
setQueueManager(" ");
//or
setQueueManager("");
The last use case above would likely work if you did not use setQueueManager at all.
If you want to view the contents of the CCDT, you can use the runmqsc command that comes as part of the MQ v8 and higher client or server install.
For Unix ksh/bash shells use the following:
export MQCHLLIB=PATH/OF/CCDT
export MQCHLTAB=NAME_OF_CCDT
runmqsc -n
For Windows use the following:
set MQCHLLIB=PATH/OF/CCDT
set MQCHLTAB=NAME_OF_CCDT
runmqsc -n
Once the runmqsc program has started and displayed Starting local MQSC for 'NAME_OF_CCDT'. you can run the following command to see the channel details:
DIS CHL(*)
Below is a more specific command to narrow the number of fields returned:
DIS CHL(*) QMNAME CONNAME
I haven't look at it in a while but I thought the correct format is:
MQQueueConnectionFactory qcf = new MQQueueConnectionFactory();
qcf.setQueueManager(qManager);
qcf.setCCDTURL(ccdt);
conn = qcf.createConnection(username, pwd);
Hi I am trying to push messages to active mq via http transport.
The active mq is behind the proxy server which need authentication before pushing the message to external active mq.
is there a way I can set the proxy details to active mq connection.
I read some article where mentioned we can use HttpClientTrasport.
But i am not sure how to set the HttpClientTrasport to ActiveMQConnection object.
Thanks in advance.
if i understand you want to set the proxy config for org.apache.activemq.ActiveMQConnection ?
this can be set on org.apache.activemq.ActiveMQConnectionFactory.ActiveMQConnectionFactory(String brokerURL) level by passing the url with proxy config like this :
brokerURL = "http://localhost:8080?proxyHost=proxy&proxyPort=8080&proxyUser=user&proxyPassword=pwd"
if you have specials characters on user or pwd you need to change like this :
brokerURL = "http://localhost:8080?"+ URLEncoder.encode("proxyHost=proxy&proxyPort=8080&proxyUser=user&proxyPassword=pwd", "UTF-8");
hope this help
add transportConnection configuration in activemq;
nano ~/apache-activemq-5.11.1/conf/activemq.xml
<transportConnector name="http" uri="http://0.0.0.0:8888?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
use broker_url
private static final String DEFAULT_BROKER_URL = "http://localhost:8888";
add dependency in project
<dependency><groupId>org.apache.activemq</groupId><artifactId>activemq-http</artifactId><version>5.11.1</version></dependency>
I have installed Websphere MQ 8 server in my Windows 8,
using MQ Explorer :
I created a queue manager MAJID.QUEUE.MANAGER with port 1419.
I created a TCP listener on port 1419.
I tried one of the java programs that Tools from MQ8 installation, it runs like this :
PCF_ListQueueNames MAJID.QUEUE.MANAGER 10.196.67.99 1419
but I only got :
Completion Code '2', Reason '2035'.
UPDATE:
the log file says :
AMQ9777: Channel was blocked
EXPLANATION:
The inbound channel 'SYSTEM.DEF.SVRCONN' was blocked from address 'ITD- 968735
(192.168.56.1)' because the active values of the channel matched a record
configured with USERSRC(NOACCESS). The active values of the channel were
'CLNTUSER(alotfi) ADDRESS(ITD-968735)'.
ACTION:
Contact the systems administrator, who should examine the channel
authentication records to ensure that the correct settings have been
configured. The ALTER QMGR CHLAUTH switch is used to control whether channel
authentication records are used. The command DISPLAY CHLAUTH can be used to
query the channel authentication records.
----- cmqxrmsa.c : 1461 -------------------------------------------------------
1/13/2016 15:55:13 - Process(9988.27) User(MUSR_MQADMIN) Program(amqrmppa.exe)
Host(ITD-968735) Installation(Installation1)
VRMF(8.0.0.4) QMgr(MAJID.QUEUE.MANAGER)
AMQ9999: Channel 'SYSTEM.DEF.SVRCONN' to host '192.168.56.1' ended abnormally.
EXPLANATION:
The channel program running under process ID 9988(8292) for channel
'SYSTEM.DEF.SVRCONN' ended abnormally. The host name is '192.168.56.1'; in some
cases the host name cannot be determined and so is shown as '????'.
There is a great MQ security blog article which describes how to configure MQ to let clients connect securely (i.e. without just turning the security features off).
However to address your specific question, the default channel authentication rules for new MQ 8 queue managers prevent client connections to the queue manager via SYSTEM.* channels. If you run DIS CHLAUTH(*) ALL on a new MQ 8 queue manager you'll see:
DIS CHLAUTH(*) ALL
2 : DIS CHLAUTH(*) ALL
AMQ8878: Display channel authentication record details.
CHLAUTH(SYSTEM.ADMIN.SVRCONN) TYPE(ADDRESSMAP)
DESCR(Default rule to allow MQ Explorer access)
CUSTOM( ) ADDRESS(*)
USERSRC(CHANNEL) CHCKCLNT(ASQMGR)
ALTDATE(2016-01-14) ALTTIME(16.15.20)
AMQ8878: Display channel authentication record details.
CHLAUTH(SYSTEM.*) TYPE(ADDRESSMAP)
DESCR(Default rule to disable all SYSTEM channels)
CUSTOM( ) ADDRESS(*)
USERSRC(NOACCESS) WARN(NO)
ALTDATE(2016-01-14) ALTTIME(16.15.20)
AMQ8878: Display channel authentication record details.
CHLAUTH(*) TYPE(BLOCKUSER)
DESCR(Default rule to disallow privileged users)
CUSTOM( ) USERLIST(*MQADMIN)
WARN(NO) ALTDATE(2016-01-14)
ALTTIME(16.15.20)
The second rule prevents all client connections to channels named SYSTEM.*. This applies to you because you are connecting to SYSTEM.DEF.SVRCONN.
You probably want to define a new SVRCONN channel for your application to connect to and use that instead of SYSTEM.DEF.SVRCONN.
When defining a new channel MQ security best practice is to set the MCAUSER field of the channel to a user that doesn't exist - for example 'nobody'. You can then define a new channel authentication rule that allows your Java application to adopt the user ID you have chosen instead of the default user 'nobody'. The rule could for example be an ADDRESSMAP rule that allows any clients connecting from a specific IP address to connect to the new channel and to adopt the user ID you have chosen.
In summary:
1) Choose a valid user that exists on your system (but that isn't in the 'mqm' group)
2) Define a new non-SYSTEM channel, with MCAUSER set to 'nobody', e.g.
DEFINE CHANNEL(MY.FIRST.CHANNEL) CHLTYPE(SVRCONN) MCAUSER('nobody')
3) Define a new channel auth rule that allows connections from the IP address of you client, and adopts the user you have defined, e.g.
SET CHLAUTH(MY.FIRST.CHANNEL) TYPE(ADDRESSMAP) ADDRESS('192.168.56.1') USERSRC(MAP) MCAUSER('validuser') ACTION(REPLACE)
You will have one further step to perform. You need to tell MQ that 'validuser' is allowed to connect, put, and/or get messages. You can use SET AUTHREC to define the authorities the client should have. See the KnowledgeCenter for the valid AUTHREC options.
The above is an example of how to configure MQ to let your client connect. You should use a combination of blog articles like the one I've linked to and the KnowledgeCenter to set up your security in the way want. For example you might want to use TLS certificates to authenticate your Java client which I haven't described above.
How can I check whether a queue exists on a JMS server using the Java API? I don't want to send or receive any data to the queue for now, just verify that the queue exists. Also, the queue may be empty.
Here is my code sample. I have removed the error handling for simplicity.
Connection connection = null;
Session session = null;
connection = factory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//I was hoping this next line would throw an exception if the queue does not exist
Queue queue = session.createQueue(queueName);
My JMS server is TIBCO EMS. I'm hoping for a solution that works on versions 5-7.
Solution
I followed the recommendation in the accepted answer but created a browser instead. The following line threw an exception as desired:
QueueBrowser browser = session.createBrowser(queue);
This is dependent on the provider, but you wont know in most cases until you create the session type, such as session.createConsumer. Simply creating a consumer this way will not consume any messages until you do a receive. And it is here the behavior may change from provider to provider and configuration of the server.
For example with ActiveMQ, assuming there are no permissions blocking the user you are connecting with, the queue is created automatically when you create the session type.
With WebSphere MQ, the queue has to be defined by an admin. If it does not exist, the queue manager will return an exception with a reason code of 2085 (UNKNOWN_OBJECT_NAME).
Outside of this, you'd need to see if the particular provider had a way to access a list of queues. Using the above examples, ActiveMQ you can get the list of queues using JMX, with WebSphere MQ, you can do this if you have permissions to send PCF commands to the queue manager.
Try creating a consumer or producer off the Session passing in the queue object you just created:
session.createConsumer(queue);
This should throw an InvalidDestinationException if the queue (or topic) does not exist.