Why these JMS blog posts?
I had a “quick” question from someone, “can I configure JMS to reduce CPU usage and improve performance?”. It was was wet Monday in Orkney (north of Scotland) and I thought I would spent an hour looking into it. A few weeks later, I am much wiser, and have some answers to the question. The Knowledge Centre has a lot of information, mostly is useful, mostly accurate, some information is missing and some assumes that you are very familiar with the product.
I also found that that the Java people tend to use different words for familiar concepts, so I had to struggle with this different language.
Below are the blog posts I wrote on getting JMS working on Ubuntu 18.04with MQ V9.
- Getting JMS samples working in batch on Ubuntu for people who cannot spell Java Massage Service.
- Getting JMS working in a WAS Liberty Web Server on Ubuntu for people who cannot spell Java Massage Service.
- Avoiding the random cut and paste approach to configuring JMS in a Liberty web server.
- JMS performance tuning in WAS Liberty
Basic background to JMS
JMS programs are written in Java (the clue is in the name)
They can run as a batch job, for example one program to put a message to a queue, and another program to get the message and send a reply back. You might have an application that connects to MQ, processes one message and ends, or a server application which connects and stays connected all day processing messages
You can run JMS applications in a Web Server
- Connect once and stay connected all day doing sends and receives
- Like a trigger monitor, a listener task gets a message from the queue and passes it to an Message Driven Bean(MDB) application which does all of the work, and sends the reply back to the requester.
- An application uses the web server and runs a transaction which invokes a program (MDB) to do the work. This is an URL usually something like http://localhost:9080/WMQ_IVT/ in your web browser.
JMS people speak a different language.
The JMS people (being Java people) speak with a different accent, which takes a bit of getting used to.
Puts vs send: MQ people speak of puts and gets, JMS people talk about send and receive.
Specifying queue names: C programmers often hard code names in their programs for example
#define SERVERQUEUE “PAYROLLSERVERQ”.
Change the common header file, and recompile it and it picks up the new defintions.
The JMS people have a better way of doing it. The mapping of SERVERQUEUE to “PAYROLLSERVERQ” can be done outside of the program. You just change a configuration file, and do not need to rebuild your program. However some Java people write programs without the use of the external parameters, and define every thing in their Java program!
Connecting to a queue manager. In a similar way, to get a C program to talk to a queue manager as a client you need to set up an environment variable containing the channel name, IP address etc.
In JMS this is usually stored in a configuration file, so to change the queue manager, or where it is located, you change the configuration and restart the application.
- In WAS Liberty the code to connect to MQ is called a connectionFactory. You pass parameters to this using data in XML in <jmsConnectionFactory …> tags. Personally, I would have called this <jmsConnectionFactoryInput… >. You just have to get used to these naming conventions.
The Russian doll of packaging java programs
If you open up a Russian Doll, you find another one inside. If you open this one, you find another one etc down to the smallest baby.
When working with Java you need to understand some of the packaging terms ( .jar, .war, .ear, MDB). At first with any new concept it sounds really hard, but you soon get used to it
- You start with a source program myprog.java. This gets processed and produces a myprog.class file. For simple programs, this class file can be executed.
- Normally your program needs other bits from libraries or other java programs for it to work. You package up your program and the java classes it needs into a .jar file. You can execute a name.jar file.
- Why .jar? Well my guess is that there are tools which creates an “archive”, packaging files together using zip. So it is natural to call it Java Archive. Also jar sounds half the word “Ja-va” and calling it .jbntrp (Java Bits Needed To Run Program) is much harder than saying .jar.
- If you want to run a Web application you need you need your .jar file or classes, and the webpages your application will use. Web pages are not java classes, so storing them in a .jar file feels wrong, so we now have .war files or Web ARchive. This is all the .class files and web pages etc zipped up.
- As well as a web application running from a URL, these applications often need to uses other components. Message Driven Beans (MDB) provide a service, they get called with data (the message), they do something, and return some data.
- If you are running a big commercial application, or Enterprise Application, you break up your huge program into bits. You can have some bits running here, and other bits running over there. Just like with CICS you can have a File Owning Region, and a Application Owning region. For your big commercial application you have Enterprise Java Beans (EJBs) Which take the .war files, and the MDBs, and other into and create an Enterprise Archive – or an .ear file.
How do you look inside these are archives?
You can use the jar command as in
jar -tvf xxxx.yyy
For example to list what is inside it you need the -t option
jar –tvf new.ear
17972 Tue Nov 13 20:09:26 GMT 2018 WMQ_IVT.war
571 Mon Jul 09 11:40:24 BST 2018 META-INF/application.xml
10984 Tue Nov 06 09:20:42 GMT 2018 WMQ_IVT_MDB.jar
17972 Tue Nov 13 20:09:24 GMT 2018 CCP.war
To break up the archive file and eXtract files within it into the current directory, use
jar –xvf new.ear
once you have extracted it, you can then use jar on the files within it
jar -tvf WMQ_IVT.war
gives
…
WEB-INF/classes/IVT.class
…
which is what is executed!
You may also see .rar which are Resource Adapter ARchive files – ok – this should be .raar – but it is called .rar to keep it simple.
As I said it is like a Russian doll with, files within files, within files.
What’s in a bean?
You will hear a lot about Java Beans.
When you create an instance of a Java object you pass parameters, such as
new currentJob("Colin Paice","retired") or
new currentJob("Colin Paice", "retired","Orkney")
and there are methods which take the varying number of parameters and do things with the data.
A Bean is a java program, and instead of passing parameters into the object you invoke methods such as
job = new currentJob(); // note no parameters are passed in
job.setName("Colin Paice");
job.setStatus("retired");
job.setAddress("Orkeny");
You can also issue
job.getName() .
to extract information.
Now comes the clever usage bit.
I can ask Java to tell me the methods a class has. I will get back [“setName”,”setStatus”,”setAddress”,”getName”..]
So now I can select all the “get….” methods, and call them in turn. This makes it great for systems management. You can process my bean, and display all of the information in it. I change the bean – and your program does not have to change – good eh?
A Message Driven Bean – is a bean with an additional method, “onMessage(…)” so we can now pass it data ( eg the content of an MQ message) to process.
Looking up parameters – JNDI
Java developers like to use an interface called JNDI get the configuration information.
This configuration could be in a database, or in the local file system – it depends on how you configure it.
As different applications can use the same repository, the JNDI uses a hierarchical naming standard. For example
application/Payroll/Server → PAYROLLONINPUT
application/LookupBalancel/Server → BALANCEINPUT
The application issues a query saying get me the queue name from “application/Payroll/Server”
there may also be
setup/configuation/constants
This means you can isolate the “application” and “setup” environments.
The Java people use the term initial context to identify the “application” or “setup” at the top of the naming ree.
Setting up the JNDI
It is good practice to set up your definitions once, and have every one use the same set. This takes time, and perhaps a database and server.
To save me time, (and help me understand the concepts) I used a directory tree on my laptop for “batch” programs”, and had the information stored within the Web Server configuration for the web server.
When using the file system as a JNDI repository, you point to a directory, and JNDI stores information within the directory.
Running the JMS “batch programs”
My system is Ubuntu 18.04 and I was running MQ V9.1.
I used the linux command find /opt/mqm -name Jms*.java to display all of the JMS sample programs.
This gave me
/opt/mqm/samp/jms/samples/JmsJndiProducer.java
/opt/mqm/samp/jms/samples/JmsBrowser.java
/opt/mqm/samp/jms/samples/JmsConsumer.java
/opt/mqm/samp/jms/samples/JmsJndiBrowser.java
/opt/mqm/samp/jms/samples/JmsProducer.java
/opt/mqm/samp/jms/samples/interactive/helper/JmsApp.java
/opt/mqm/samp/jms/samples/JmsJndiConsumer.java
I was interested in the JmsJndi* examples.
There are /opt/mqm/samp/jms/samples/JmsJndiProducer.class etc ready to run.
The syntax of the command is
JmsJndiProducer -i JNDI-Directory -c myQCF -d myQueue
Where the parameters summarised immediately below, and explained in more detail in the following text
- -i JNDI-Directory is the directory where the JNDI data is stored (or the URL where the database is)
- -c myQCF is the name of the connection factory (connection information object)
- -d myQueue is the label which points to which queue to use
You define resources in the JNDI file using the JMSAdmin tool.
You need the ibmmq-java* package installed to get the JMS stuff.
First you need to define where your JNDI resources will be stored.
mkdir ~/JNDI-Directory
See Starting the administration tool
The JMSAdmin tool needs to be told where the JNDI repository is.
MQ provides a file /opt/mqm/java/bin/JMSAdmin.config. Read it, there are lots of comments.
You can use the command grep -v “#” /opt/mqm/java/bin/JMSAdmin.config to display just the non comment lines. This gave me
INITIAL_CONTEXT_FACTORY=com.sun.jndi.fscontext.RefFSContextFactory
PROVIDER_URL=file:///home/colinpaice/JNDI-Directory
SECURITY_AUTHENTICATION=none
create a file JMSAdmin.config in your home directory and add the above statements to it.
INITIAL_CONTEXT_FACTORY=com.sun.jndi.fscontext.RefFSContextFactory
says it is to read JNDI from a file in the file system.
Change PROVIDER_URL=file:///home/colinpaice/JNDI-Directory to match the directory you created above.
and make it writable
chmod 744 JMSAdmin.config
Objects required to run the sample programs
- The applications get passed a connectionFactory object, I’ll use ConnectionFactory CF1. This connection is to use bindings mode to queue manager QMA, and not use a client.
- Define a queue. The application will use JMSQ1, and this maps to queue Q1 on queue manager QMA.
Run the JMSAdmin tool to create the resources, using the above JMSAdmin configuration file.
See Configuring JMS objects using the administration tool. The syntax of the commands and the options is here.
/opt/mqm/java/bin/JMSAdmin -v -cfg JMSAdmin.config
def CF(CF1) QMGR(QMA) TRANSPORT(BIND)
# Define Queue Q1
InitCtx> DEF Q(JMSQ1) QMGR(QMA) QUEUE(Q1)
dis CTX gives
InitCtx> DIS CTX
JMSADM4089 InitCtx
.bindings java.io.File
a CF1 com.ibm.mq.jms.MQConnectionFactory
a JMSQ1 com.ibm.mq.jms.MQQueue
3 Object(s)
0 Context(s)
3 Binding(s), 2 Administered
end
You can use the command DIS CF(*) and DIS Q(*) to display the objects
We have created a ConnectionFactory called CF1, and a queue object JMSQ1 which maps to queue Q1 on queue manager QMA.
To do things properly I should define a proper hierarchy
InitCtx> def ctx(Application)
InitCtx> change ctx(Application)
InitCtx/Application>def Q(APJMSQ1) QMGR(QMA) QUEUE(APPQ)
InitCtx/Application> change ctx(=UP)
InitCtx> dis ctx
.bindings java.io.File
a APJMSQ1 com.ibm.mq.jms.MQQueue
2 Object(s)
0 Context(s)
2 Binding(s), 1 Administered
end
In an application I would normally create the connectionFactory as Application/CF1 and the queue as Application/APJMSQ1
If you list the directory, you can see some of the elements
colinpaice@colinpaice:~$ ls -ltra JNDI-Directory
-rw-r--r-- 1 colinpaice colinpaice 15133 Oct 23 15:32 .bindings
drwxr-xr-x 57 colinpaice colinpaice 4096 Oct 26 08:31 ..
drwxr-xr-x 3 colinpaice colinpaice 4096 Oct 26 13:40 .
drwxr-xr-x 2 colinpaice colinpaice 4096 Oct 26 13:42 Application
Run the JMS sample program
The syntax is
JmsJndiProducer -i JNDI-Directory -c myQCF -d myQueue
so we have
- -i file:///home/colinpaice/JNDI-Directory
- -c CF1
- -d JMSQ1
The whole command is
java -cp /opt/mqm/samp/jms/samples:/opt/mqm/java/lib/com.ibm.mqjms.jar -Djava.library.path=/opt/mqm/java/lib64 JmsJndiProducer -i file:///home/colinpaice/JNDI-Directory -c CF1 -d JMSQ1
This produced
Initial context found!
Sent message:
JMSMessage class: jms_text
JMSType: null
JMSDeliveryMode: 2
JMSDeliveryDelay: 0
JMSDeliveryTime: 1540558478659
JMSExpiration: 0
JMSPriority: 4
JMSMessageID: ID:414d5120514d41202020202020202020cdb6d25b09202925
JMSTimestamp: 1540558478659
JMSCorrelationID: null
JMSDestination: queue://QMA/Q1
JMSReplyTo: null
JMSRedelivered: false
JMSXAppID: java
JMSXDeliveryCount: 0
JMSXUserID: colinpaice
JMS_IBM_PutApplType: 6
JMS_IBM_PutDate: 20181026
JMS_IBM_PutTime: 12543866
JmsJndiProducer: Your lucky number today is 641
SUCCESS
To read the message run the consumer JMS application
java -cp /opt/mqm/samp/jms/samples:/opt/mqm/java/lib/com.ibm.mqjms.jar -Djava.library.path=/opt/mqm/java/lib64 JmsJndiConsumer -i file:///home/colinpaice/JNDI-Directory -c CF1 -d JMSQ1
I have successfully managed to use a JMS program in batch to put and get a message!
Doing the above was relatively painless – the hardest part was understanding the concepts and strange language.
5 thoughts on “Getting the IBM JMS samples working in batch on Ubuntu for people who cannot spell Java Massage Service.”