File names in JCL

As a result of reading Lyn Elkins’ posts about how she struggled with the quotation marks required to specify a file name as a parameter in a JCL PARM string, I thought I would write a little summary. I personally prefer to use DD cards for my file names whenever possible as these avoid the need for quotation marks and leave your eye less likely to be squinting by the time you are done. I will show the fully qualified example right at the end for anyone who really wants it though. I am using QLOAD as an example program here, but any application that simply does an fopen() can make use of the same syntax. For example, when supplying a script in a file using MQSCX, providing an input file to load onto a queue in QLOAD, or some string data to become message data in the Q program. Any parameter that expects a file name can also instead take a string of the format “DD:card-name” (note that you MUST use capital letter D’s here). All of these use the same syntax as illustrated here.

//QLOAD    JOB
//*********************************************************************
//* QLOAD Load messages onto Q1
//*********************************************************************
//LOADTOQ  EXEC PGM=QLOAD,
//         PARM=('-m MQG1 -o Q1 -f DD:INFILE1')
//STEPLIB  DD DSN=GEMUSER.USER.LOAD,DISP=SHR                         
//         DD DSN=IBM.MQ.SCSQAUTH,DISP=SHR                           
//         DD DSN=IBM.MQ.SCSQANLE,DISP=SHR                           
//SYSPRINT DD SYSOUT=*  
//SYSOUT   DD SYSOUT=*      
//INFILE1  DD DSN=GEMUSER.DATA.MSGFILES(EXMPL1),DISP=SHR
//INFILE2  DD DSN=GEMUSER.DATA.OUTPUTQ1,DISP=SHR 
//INFILE3  DD PATH='/u/gemuser/Q1.qld',PATHOPTS=(ORDONLY)          
//MQGEML   DD DSN=GEMUSER.MQGEM.LIC,DISP=SHR
//

Here you can see that the JCL PARM string does not need any extra quotes at all, just the string DD:INFILE1 where INFILE1 is the name of a DD card in the JCL job. The DD card can then specify a fully qualified PDS member, MVS file, or HFS file as the three examples show.

To do the same job as shown in these three examples by providing the file name inline in the JCL PARM string, things start getting complicated by the fact that extra quotation makes are needed to escape the quotes since the whole JCL PARM string is surrounded by single quotes itself. This is what makes it so problematic, and why I always use DD cards instead.

If you take advantage of that the fact that MVS knows your TSO login and will pre-pend it to the MVS file name you supply, you can avoid quite a number of the quotation marks needed. The following two EXEC statements are equivalent when the TSO login is GEMUSER. The way you tell MVS not to pre-pend your TSO login is to surround the name you supply with single quotes, which turn into doubled up single quotes when used inside a JCL PARM string! The whole thing also needs to be enclosed in double quotes because of the slashes which are considered a delimiter to separate a JCL PARM string into run-time options and parameters to pass to the program.

//LOADTOQ  EXEC PGM=QLOAD,
//         PARM=('-m MQG1 -o Q1 -f"//DATA.MSGFILES(EXMPL1)"')
//LOADTOQ  EXEC PGM=QLOAD,
//         PARM=('-m MQG1 -o Q1 -f"//''GEMUSER.DATA.MSGFILES(EXMPL1)''"')

The two slashes shown in both the above example JCL PARM strings are how you tell fopen() that you are referencing an MVS file (to distinguish it from an HFS file). Here is an example EXEC statement using an HFS file instead. Again, this string needs to be surrounded by double quotes because of the slashes. The second example has no slashes, and will end up in your current directory. In fact any string that does not start with DD: or a slash (or two) will be interpreted as an HFS file in your current directory, and is why you might end up with an HFS file called “dd:SUMMARY” if you forgot to use capital letter D’s.

//LOADTOQ  EXEC PGM=QLOAD,
//         PARM=('-m MQG1 -o Q1 -f"/u/gemuser/Q1.qld"')
//LOADTOQ  EXEC PGM=QLOAD,
//         PARM=('-m MQG1 -o Q1 -fQ1.qld')

Well, I hope that helps, and also probably convinces you that DD cards are the way to go.

I didn’t invent this stuff, I’ve just spent a lot of time using it. Here is some reference material that helped me get to grips with it. Perhaps it can help you too.

On a separate note, if you ever have a long JCL PARM string, don’t struggle with line continuations, move over to using PARMDD – see JCL with long PARM strings

Is there a way to clear an MQ queue of all messages?

We were recently asked how to clear a queue of all messages within one of our tools.

In this post we cover how to do this using MQGem tools as several of them offer this capability. Open up the twisty for each tool to read how to do this.

Using MO71

There are two options for clearing a queue of all messages when using MO71. One directly interacts with the messages in a Message List dialog, and the other uses the Clear Queue command via the command server. The latter can fail if someone else has the queue open.

MO71: Using the Message List dialog

In MO71 you can browse a list of messages on a queue, then in the context menu of that dialog choose Message Selection → Apply to all Messages. Then press the Delete All button, or select Delete All from the right-mouse button context menu and all the messages will be deleted.

Delete all messages using MO71

Take care when deleting messages from a production queue manager. Consider that it might be safer to Move the message(s) to a separate holding queue, just in case the message proves to be important after all. MO71 makes it simple to Move messages too. Just fill in the holding queue name, and then press the Move All, rather than Delete All, button.

MO71: Using the Clear Queue command

From a Queue List dialog in MO71, you can choose to clear a queue from the context menu. As a destructive command, this will show a confirmation dialog before it goes ahead.

Delete all messages using the Clear command in MO71

Remember that this command can fail if the queue is open by another application at the time.

Using the Q program

You can use the Q program to quickly destructively get off all the messages on a queue, to ensure it is empty before you begin using it for something else. This can work even when the queue is in use (unless it is exclusively in use of course) and the CLEAR QLOCAL command can’t be used.

Here is an example which destructively gets (-I) from Q1 and does not print the messages to the screen (-q), aka ‘quiet’ mode:-

q -m MQG1 -I Q1 -q

Take care when deleting messages from a production queue manager. Consider that it might be safer to Move the messages to a separate holding queue, just in case they prove to be important after all. This is simple to achieve using a command like the following:

q -m MQG1 -I Q1 -o HOLDING.Q
Using QLOAD

You can use QLOAD to quickly destructively get off all the messages on a queue, to ensure it is empty before you begin using it for something else. This can work even when the queue is in use (unless it is exclusively in use of course) and the CLEAR QLOCAL command can’t be used.

Here is an example which destructively gets (-I) from Q1 and discards them (-f null), aka sends them to the null file destination:-

qload -m MQG1 -I Q1 -f null

Take care when deleting messages from a production queue manager. We would recommend using QLOAD to actually move the messages to an output file, in case you change your mind about deleting them!

qload -m MQG1 -I Q1 -f c:\temp\Q1deletedmsgs.qld 
Using MQEdit

In MQEdit you can browse a list of messages on a queue, then in the context menu of that dialog choose Message Operations → Apply to all Messages. Then press the Delete All button, or select Delete All from the right-mouse button context menu and all the messages will be deleted.

Delete all messages using MQEdit

Take care when deleting messages from a production queue manager. Consider that it might be safer to Move the message(s) to a separate holding queue, just in case the message proves to be important after all. MQEdit makes it simple to Move messages too. Just fill in the holding queue name, and then press the Move All, rather than Delete All, button, or simply Drag and Drop the messages onto the new queue.

Using MQSCX

You can of course issue the MQSC CLEAR QLOCAL(q-name) command from MQSCX. However, it can go one better than this. The CLEAR QLOCAL command is problematic because it can fail when someone else has the queue open, even if it is not open in a way that stops you getting messages. Instead you can issue the following command in MQSCX:-

=clear qlocal(Q1)

then MQSCX will first attempt the CLEAR QLOCAL command and if that fails with "object in use" then it will switch to getting the messages off the queue instead. You can read more about this in MQSCX: Clearly a better way.


If you don't have any of these tools, but would like to try any or all of them out, please contact support@mqgem.com and a 1-month trial licence will be sent to you with no obligation to buy. You can download the tools from our website.

Displaying only non-system queues with an MQSC command

IBM Support recently released a Support Doc which provided a variety of examples on how to use the WHERE clause on MQSC DISPLAY commands. The final example was actually illustrating something that the WHERE clause could not do and showing how to use alternate UNIX script methods such as grep, xargs, cut and tr to work round the limitations of the WHERE clause in that instance.

Our MQSCX product also has a WHERE clause, with keyword =WHERE to distinguish it from the IBM MQ WHERE clause. Our aim with the =WHERE clause was to remove some of the restrictions of the IBM MQ WHERE clause. The example in question is one of those restrictions we lifted.

Here is the direct quote of the example from the Support Doc.

Question: Displaying only non-system queues (do not display SYSTEM.* queues)

You wish to see all non-system queues (for example LOCAL QUEUES) using DISPLAY command.

It is not possible to do this query with the SQL-like facilities in the WHERE clause.

The following was found in a forum of MQ users, and it is added here for completeness.

For Unix (it is a SINGLE line – very long line!):

echo "dis ql(*)" | runmqsc QM92 | grep QUEUE | tr '()' ' ' | tr -s ' ' | cut -f3 -d' ' | grep -v ^SYSTEM | xargs -I %%
echo "dis ql(%%)" | runmqsc QM92

Compare this to the command you can use in MQSCX to achieve the same, a list of all the queues which do not start with the name SYSTEM:

DISPLAY QLOCAL(*) =WHERE(QUEUE NL 'SYSTEM.*')

This uses the NL (Not-Like) operator which is the opposite of the LK (Like) operator. These two operators work with wildcarded string values.

The reason it was not possible to do this with the IBM MQ WHERE clause is because the name of the object is disallowed from the WHERE clause. This is one of the restrictions we lifted when creating the =WHERE clause.


If you don’t already have MQSCX and you’d like to try it out, please email support@mqgem.com to request a trial licence. You can download MQSCX from our website.

Latest MQEV and MQSCX released on z/OS

Our recently released versions of MQEV V9.2.2 and MQSCX V9.2.1 are now also available on z/OS.

Read more about these versions here:-


The new versions can be downloaded from the MQEV Download Page and the MQSCX Download Page. Any current licensed users of these products can run the new version on their existing licence. If you don’t have a licence and would like to try out MQEV or MQSCX then send an email to support@mqgem.com and a 1-month trial licence will be sent to you.

MQSCX: Clearly a better way

A new version of MQSCX was recently released, and one feature in that release was the introduction of a new MQSCX command, =clear qlocal.

Have you ever issued the MQSC CLEAR QLOCAL command and been told:-

AMQ8148E: IBM MQ object in use.

This can happen even if the queue is open for shared input or just for output. It can be very annoying if you have to stop a program that is holding a queue open, just to be able to clear it of messages.

The =clear qlocal works round this by operating in the following way:-

  • Attempt the MQSC CLEAR QLOCAL.
  • If this fails with the above error, then switch to using MQGET to remove the messages off the queue.

That’s the simple explanation, however, if you want more gory details, open the twisty below.

The gory details
  • What happens if the initial MQSC CLEAR QLOCAL fails for some other reason?
    In this case the command does not continue to the MQGET phase. This might be a security failure for example, so if you are not authorised to issue the MQSC CLEAR QLOCAL command, then that effectively carries over into this command as well.
  • I don’t want to issue millions of MQGETs if the MQSC CLEAR QLOCAL fails!
    The MQSCX =clear qlocal command has some additional parameters, one of them is depth() which sets the maximum depth of the queue that does not require a user confirmation to proceed. It defaults to 1000 messages. So it will do 1000 MQGETs without asking for you to confirm, but if there are 1001 messages, it will first prompt to confirm you wish to go ahead.
  • I don’t want a manual prompt – I’m sure, just get on with it!
    The MQSCX =clear qlocal command has one more parameter which is confirm(yes | no). This will be useful in scripts where you don’t want to have a human interaction, but just want the command to go ahead regardless of how many messages there are on the queue – just get it emptied.
  • What happens if the queue is opened exclusively?
    Unfortunately, there’s no magic here. If the queue is opened exclusively, neither the MQSC CLEAR QLOCAL command, nor an application MQGET can do anything to the queue. So make sure that your queues are not defined with DEFSOPT(EXCL) and NOSHARE unless they need to be – I’m looking at you z/OS queue managers!
  • So how does the full command look?
    The full command looks like this:-
    =clear qlocal(<queue name>)
           [confirm (yes | no)]
           [depth(<queue depth threshold>)]

Summary

Never struggle to empty a queue again – a swift =clear qlocal(queue name) and all is done.


If you don’t already have MQSCX and you’d like to try it out, please email support@mqgem.com to request a trial licence. You can download MQSCX from our website.

Text Files in Preview Pane

If you’re anything like me, you have a number of text files on your Windows machine that do not use the .txt file extension.

For all of these I have associated Notepad as the application to be used by default when I double click on these file types from the Windows File Explorer. I’m probably a 70:30 command line vs File Explorer user, but it is from time to time handy to be able to double click too.

What I’d never managed to figure out though (until today) was how to get the preview pane in Windows File Explorer to display these text formats with different file extensions. Now that I have found out how to do it, I thought I would share it with all of you too.

Windows File Association Dialog

Windows File Association Dialog

Firstly, it’s important to have the file association in place first, specifically the “Always use this app to open … files” checkbox. This seems to be what adds the file extension into the list you are about to manipulate. So take a moment to make sure all the extensions you want to preview have a default application set.

Now open the registry editor and navigate to Computer > HKEY_CLASSES_ROOT. In here you will see a list of all the file extensions your machine knows about. Locate your file extension that you want to preview (remember you can type into the entry field just below the menu bar). Select the file extension, in this example I am fixing up the .mqx extension; and on the right hand pane right-click and select New > String Value. Give the new key the name “PerceivedType” and then modify it to give it the data value of “text”.

Adding a new String Value in Windows Registry

Adding a new String Value in Windows Registry

Giving new registry key a value of text

Giving new registry key a value of text

Now go back to your File Explorer and you should be able to see your .mqx file in the preview pane.

View .mqx file in Preview Pane

View .mqx file in Windows File Explorer Preview Pane


Many thanks to Scott Williams who wrote about this in a blog post in 2013 which I finally stumbled across in 2020 and it made my day!

Working with JSON CCDT files

A recent feature added to IBM MQ are Client Channel Definition Table (CCDT) files in JSON format.

One of the features of MQSCX is to work with CCDT files, both the binary format, and now (since MQSCX V9.2.0) the JSON format.

With MQSCX you can display or create/alter a JSON CCDT file using familiar MQSC commands. Your tried and tested scripts for creating binary format CCDT files, can be used to create the same in JSON format.

For example, this file:

DEFINE CHANNEL(MQG1.SVRCONN) CHLTYPE(CLNTCONN) +
       CONNAME('gemmvs(1701)') QMNAME(MQG1)
DEFINE CHANNEL(MQG2.SVRCONN) CHLTYPE(CLNTCONN) +
       CONNAME('gemmvs(1702)') QMNAME(MQG2)

ClientChls.mqs

Run with this command:

mqscx -j -i ClientChls.mqs

Will produce a JSON CCDT file like this where the MQCHLLIB and MQCHLTAB environment variables point:

ClientChls.json
{
  "channel":
  [
    {
      "name": "MQG1.SVRCONN",
      "type": "clientConnection",
      "clientConnection":
      {
        "connection": 
        [
          {
            "host": "gemmvs",
            "port": 1701
          }
        ],
        "queueManager": "MQG1"
      }
    },
    {
      "name": "MQG2.SVRCONN",
      "type": "clientConnection",
      "clientConnection":
      {
        "connection": 
        [
          {
            "host": "gemmvs",
            "port": 1702
          }
        ],
        "queueManager": "MQG2"
      }
    }
  ]
}

You can also use MQSCX interactively and type in these familiar MQSC commands, using TAB auto-complete to assist, and create a JSON CCDT that way too.

Validation

One of the benefits of using a JSON format CCDT is that it is a text file instead of the proprietary binary file. So why do you need a tool like MQSCX to create one? Well the answer is validation. If you create a JSON CCDT file by hand, you may have a typo or spelling error in any of the field names. If you do this and display your file using runmqsc, or run it using an MQ Client, neither will inform you of your typo because they are designed to be future proof. There is no versioning on a JSON CCDT file, so if the MQ Client uses one that is from a newer version of MQ, it just ignores anything that it doesn’t recognise. This includes your misspelled attributes, that you actually wanted it to use!

Misspelled attributes

Here’s an example JSON CCDT with a typo:

{
  "channel":
  [
    {
      "name": "MQGEM.SVRCONN",
      "type": "clientConnection",
      "clientConnection":
      {
        "connection":
        [
          {
            "host": "localhost",
            "port": 1701
          }
        ]
      },
      "connectionManagement":
      {
         "hbInterval": 60
      }
    }
  ]
}

HBintClnt.json

If I use runmqsc to look at it, it won’t complain and it will tell me that my heartbeat interval value is 300.

Note: runmqsc can be used to display the contents of the JSON CCDT file, but not to create or edit its contents.

runmqsc -n
5724-H72 (C) Copyright IBM Corp. 1994, 2019.
Starting local MQSC for 'HBintClnt.json'.
DISPLAY CHANNEL(*) HBINT
     1 : DISPLAY CHANNEL(*) HBINT
AMQ8414I: Display Channel details.
   CHANNEL(MQGEM.SVRCONN)                  CHLTYPE(CLNTCONN)
   HBINT(300) 

With a SVRCONN defined with HBINT(60), a client running using this CCDT, which should be using 60 seconds for the heartbeat interval, will actually use 300, and so the running SVRCONN will end up with 300 as well.

AMQ8417I: Display Channel Status details.
   CHANNEL(MQGEM.SVRCONN)                  CHLTYPE(SVRCONN)
   CONNAME(127.0.0.1)                      CURRENT
   HBINT(300)                              STATUS(RUNNING)
   SUBSTATE(RECEIVE)                    

Running MQSCX to look at this JSON CCDT file, will show the following:

MQSCX Extended MQSC Program – Version 9.2.0

At line 20 of file ‘C:\MQGem\HBintClnt.json’, unrecognised field name ‘hbInterval’ found

CCDT file ‘C:\MQGem\HBintClnt.json’, 1 channels read

Licenced to Paul Clarke

Licence Location: Head Office

So, you are immediately aware of the issue in your CCDT file.

Missing mandatory attributes

There aren’t many mandatory attributes on a client channel definition, but those that are, are important!

If you omit a mandatory attribute, again runmqsc won’t complain, but of course the client channel will when it tries to make a connection.

Here’s an example JSON CCDT with a channel that hasn’t specified a connection name:

{
  "channel":
  [
    {
      "name": "MQGEM.SVRCONN",
      "type": "clientConnection",
    }
  ]
}

ConnameClnt.json

When a client application attempts to run using this, you’ll get something like the following in your client AMQERR01.LOG:

AMQ9203E: A configuration error for TCP/IP occurred.

EXPLANATION:
Error in configuration for communications to host ' '.  Allocation of
a TCP/IP conversation to host ' ' was not possible.

Running MQSCX to view, and therefore validate, this JSON CCDT file, will show the following:

MQSCX Extended MQSC Program – Version 9.2.0

The channel definition ending at line 7 of file ‘C:\MQGem\ConnameClnt.json’ has not specified a connection name

CCDT file ‘C:\MQGem\ConnameClnt.json’, 1 channels read

Licenced to Paul Clarke

Licence Location: Head Office

Enhanced Filtering

In addition to the above, using MQSCX to view and manipulate your CCDTs (both binary and JSON) allows you to utilise the power of MQSCX filtering. For example you can use a command like the following, which wouldn’t be allowed through runmqsc.

DISPLAY CHANNEL(*MQGEM*)

Or you could use enhanced WHERE clauses that are more powerful than the IBM MQ supplied WHERE clause. For example, you can use a command like the following:

DISPLAY CHANNEL(*) =WHERE(NOT(SCYEXIT AND SCYDATA))

Re-ordering your JSON defined channels

The order that an MQ client will pick channels to use from your JSON CCDT is dependant on the order they are in the file, and you control that order. In the binary CCDT, the file was always internally ordered alphabetically, but with a text file like the JSON CCDT you are in complete control.

As it is a text file, you can just use your favourite editor to copy the lines around, just make sure you have all the right brackets and commas in place. Alternatively, you can use MQSCX to reorder your file just how you want it as follows.

MOVE CHANNEL(MQG1.SVRCONN) POSITION(1) NEWPOS(2)

You can also indicate the position in the file for a channel definition at the time you create it, for example, using a command like this to make the new channel first in the file:

DEFINE CHANNEL(MQG1.SVRCONN) CHLTYPE(CLNTCONN) +
       CONNAME('gemmvs(1701)') QMNAME(MQG1) INDEX(BEFORE)

JSON file format

You also have some control over the contents of the JSON CCDT file that is created by MQSCX with three options on how the fields are output.

  • =set jsonfields(all)
    This option will create a JSON CCDT file with all the values output, even those that just contain the default or blank values. This makes your JSON CCDT file bigger of course, but might be useful if you need to manually edit it later and you are unsure of the field names.
  • =set jsonfields(date)
    This option will create a JSON CCDT file with the minimum set of fields (see below) and additionally the alteration date/time field. The MQ Client does not make use the of this date information, so it is only added if you ask for it. We assume, since it is in the JSON CCDT schema that someone might want it for something.
  • =set jsonfields(min)
    This is the default setting, and only outputs the channel fields are have non-default or non-blank values. This will make your JSON CCDT file much smaller than you would get with jsonfields(all), and is the minimum the MQ Client needs to work with.

Summary

So if you’re starting to look into using JSON CCDT files, perhaps for their ability to define channels with the same name within one CCDT, and you’re not a JSON format expert, take a look at MQSCX to produce your CCDTs and ensure that you always have a valid format CCDT file to use with your MQ Clients.


If you don’t already have MQSCX and you’d like to try it out, please email support@mqgem.com to request a trial licence. You can download MQSCX from our website.

MQSCX on z/OS

The recent release of MQSCX V9.2 included the provision of MQSCX to run natively on the z/OS platform.

All those features of MQSCX that you have been able to use on your distributed platforms, such as report writing, functions, foreach loops (and all the other great MQSCX control language features); namelist processing and more, are now available natively on z/OS

MQSCX can be run interactively in a number of environments on your z/OS system. Type in a command and immediately get responses back. Additionally in these environments you can also run scripts that are stored in MVS files or HFS files.

  • z/OS UNIX (TTY)
    In this environment, you are running with a TTY terminal and MQSCX will look and feel very similar to the experience you have on a Unix or Windows platform. You have tab auto-complete for commands and object names, and scrolling of output.
  • z/OS UNIX (z/OS shell)

    From TSO/E, use the OMVS command to run the z/OS shell. You can use the MQSCX program interactively in this environment.

    GEMUSER:/u/gemuser/mqscx: >mqscx -m MQG1   
    MQSCX Extended MQSC Program - Version 9.2.0 
    Licenced to MQGem Software Limited          
    Licence Location: Any location               
                                                 
    Connected to 'MQG1'                          
    MQG1>DISPLAY QMGR VERSION                 
    CSQM409I %MQG1                               
    QMNAME(MQG1)        VERSION(09010500)        
    MQG1>
     ===> =import file(//MQSCX.SCRIPTS(AGE)) 
                                                                       INPUT
    ESC=¢ 1=Help     2=SubCmd   3=HlpRetrn  4=Top      5=Bottom   6=TSO
          7=BackScr  8=Scroll   9=NextSess 10=Refresh 11=FwdRetr 12=Retrieve
  • TSO/E

    From the TSO READY prompt, or from the ISPF command processor panel (aka Option 6), you can use the MQSCX program interactively. You must remember to tell the CALL command that you don’t want its default behaviour of upper casing the parameter string.

       Menu  List  Mode  Functions  Utilities  Help             
     ─────────────────────────────────────────────────────────────────────
                                   ISPF Command Shell           
     Enter TSO or Workstation commands below:                   
                                                                
     ===> CALL USER.LOAD(MQSCX) '-m MQG1' ASIS                            
                                                                          
                                                                          
                                                                
     Place cursor on choice and press enter to Retrieve command 
                                                                
     => CALL USER.LOAD(MQSCX) '-m MQG1' ASIS                    
     =>                                                         
     MQSCX Extended MQSC Program - Version 9.2.0                
     Licenced to MQGem Software Limited                         
     Licence Location: Any location                             
                                                                
     Connected to 'MQG1'                                        
     MQG1>                                                      
    DISPLAY QMGR VERSION                                        
     CSQM409I %MQG1                                             
     QMNAME(MQG1) VERSION(09010500)                             
     MQG1>                                                      
    =import file(//MQSCX.SCRIPTS(AGE))

As well are providing an interactive MQSC experience, MQSCX can also run scripts in interactive mode, as shown above, and also from batch in JCL jobs.

  • JCL to run a script from a file

    While it is possible to provide the file name directly when running a script, it is much simpler (and shorter) to use a DD name in JCL. This DD name can point to an MVS file or an HFS file as the two example DD names show below.

    //MQSCX    JOB                                                         
    //*********************************************************************
    //* Run an MQSCX script                                                
    //*********************************************************************
    //MQSCX    EXEC PGM=MQSCX,                                             
    //         PARM=('-m MQG1 -i DD:SCRIPT')                               
    //STEPLIB  DD   DSN=GEMUSER.USER.LOAD,DISP=SHR                         
    //         DD   DSN=IBM.MQ.SCSQAUTH,DISP=SHR                           
    //         DD   DSN=IBM.MQ.SCSQANLE,DISP=SHR                           
    //SYSPRINT DD   SYSOUT=*                                               
    //SYSOUT   DD   SYSOUT=*                                               
    //MQGEML   DD   DSN=GEMUSER.MQGEM.LIC,DISP=SHR                         
    //SCRIPT   DD   DSN=GEMUSER.MQSCX.SCRIPTS(AGE),DISP=SHR                
    //SCRIPT2  DD   PATH='/u/gemuser/mqscx/scripts/Age.mqx',
    //         PATHOPTS=(ORDONLY)                            
    //
  • JCL to run a script inline

    While developing your script, you might find it easier to have it inline in the same JCL job you use to submit it. You can either use the SYSIN DD card, or provide a DD name as above, and inline that DD card.

    //MQSCX    JOB                                                         
    //*********************************************************************
    //* Run an MQSCX script                                                
    //*********************************************************************
    //MQSCX    EXEC PGM=MQSCX,                                             
    //         PARM=('-m MQG1')                               
    //STEPLIB  DD   DSN=GEMUSER.USER.LOAD,DISP=SHR                         
    //         DD   DSN=IBM.MQ.SCSQAUTH,DISP=SHR                           
    //         DD   DSN=IBM.MQ.SCSQANLE,DISP=SHR                           
    //SYSPRINT DD   SYSOUT=*                                               
    //SYSOUT   DD   SYSOUT=*                                               
    //MQGEML   DD   DSN=GEMUSER.MQGEM.LIC,DISP=SHR                         
    //SYSIN    DD   *                          
    foreach(DISPLAY QLOCAL(*) CURDEPTH)        
      if (CURDEPTH > 0)                        
        print QUEUE,"has",CURDEPTH,"messages!" 
      endif                                    
    endfor   
    /*                                         
    //

We have endeavoured to make MQSCX work appropriately in each environment on z/OS, but would of course value any feedback users have to improve how MQSCX operates in any and all of these environments.


You can download MQSCX for z/OS from the MQSCX Download Page. If you’d like to try out MQSCX on z/OS, please email support@mqgem.com and a 1-month trial licence will be sent to you.

MQSCX version 9.2.0 is released

MQGem Software is pleased to announce that a new version of MQSCX, our command line extended MQSC tool for IBM MQ, is now available.

The main features of the release are as follows:-

JSON CCDT editing

MQSCX has always been able to create and edit the IBM MQ binary CCDT files. However, now you can display, edit and create CCDT files using the JSON format. This is extremely useful to more easily create valid JSON CCDT files, knowing that you haven’t misspelled an attribute, or put an attribute in the wrong place in the schema. You can create JSON CCDT files using the familiar MQSC commands DEFINE CHANNEL, just as before, with one or two additional attributes to handle those times when there are multiple channels of the same name, or when you want to change the order of the channels in the JSON CCDT file.

Read more about this new feature here.

MQSCX control language updates

  • New and changed expression functions
    The MQSCX control language has some new and changed expression functions in this new release.

    • New: valueof()
      String function to parse files in the format FIELD(VALUE)This could be very useful for parsing optional parameters into a function. For example, you could imagine having the string
      QMGR(MQG1) PRINT(N) REPEAT(10)
      and then using the following script to use those parameters to control how a function might work.

      func Report(ParmStr)
        @qmgr   = valueof("QMGR", @ParmStr)
        @print  = valueof("PRINT", @ParmStr)
        @repeat = eval(valueof("REPEAT", @ParmStr))
        if (!exists(@repeat))
          @repeat = 2
        endif
        :
      endfunc
    • New: power(x,y)
      Mathematical function returning the value of xy.For example power(10,2) = 100.00
       
    • Changed: system()
      The system() function now has an optional second parameter to determine whether to run the invoked command synchronously or asynchronously.This can be especially useful if you need to know that your command has completed before you continue with the rest of the script.

      @CmdStr = "q -m " + _qmgr + ' -o LOG.Q -M"Job Done!"'
      system(@CmdStr, const.SYNC)
  • New and updated system variables
    The MQSCX control language has some new and changed system variables in this new release.

    • New: _lastcmdresp
      This system variable will contain any error message associated with the last command issued. For example:

      foreach(RESET QSTATS(*))
        :
      endfor
      if (_numEach = 0)
        print _lastcmdresp
      endif
    • Server O/SUpdated: _os
      This can now contain the value “MVS” when running MQSCX on z/OS. Read this post for more on this system variable.
       
  • Command substitution improvements
    Previously you could not use an array value in a command substitution. That restriction has now been lifted. For example, you can now do this.

    @Name[1] = "Q1"
    @Name[2] = "Q2"
    @index = 2
    DISPLAY QLOCAL(<@Name[@index]>)

Force the config file to be written

Previously, the config was not saved when you ran in file mode. This was to avoid any settings used by an imported script file from changing your setup. However, there are times when you want an imported script to save the changes, for example if redefining synonyms. So now, there is a new flag, -S that will force the config to be saved regardless.

Remove weak cipher values

IBM MQ is strongly discouraging users from selecting weak CipherSpec values on their channels. All deprecated CipherSpec values will now not be shown to MQSCX users when tabbing through all the possibilities on the SSLCIPH attribute. You can change this behaviour using the =set weakciph(on | off) command.

z/OS Support

MQSCX is now available to run locally on the z/OS platform. To enable this you require a z/OS specific licence. A distributed platform MQSCX licence will not enable MQSCX on z/OS to run. For more on this read this blog post: MQSCX on z/OS

Command Level support up to 920

MQSCX now supports queue managers up to the most recently released command level of 920.


The new version can be downloaded from the MQSCX Download Page. Any current licensed users of MQSCX can run the new version on their existing licence. If you don’t have a licence and would like to try out MQSCX then send a note to support@mqgem.com and a 1-month trial licence will be sent to you.

Simple Scripting of the Current Queue Depth

On StackOverflow, someone asked a question about determining when a queue’s current depth is the same as maximum depth. Roger, from Capitalware, and I agree on one thing here and that is sed and awk are likely to be able to do it, but that we’d rather do it another way.

Roger provided a Java program to get both the current and maximum depths and compare the values – he has supplied his source code here.

I would do it by using our MQSCX tool. In that case it is possible to issue the following command (which in fact is exactly what the Original Poster was trying to do with runmqsc). The difference being I am using the MQSCX =WHERE clause which is very much more powerful than the IBM MQ supplied WHERE clause.

DIS QL(*) =WHERE(CURDEPTH EQ MAXDEPTH)

Also, if you want to find all those queues that are fuller than the Queue-High threshold, then you could use a command like this.

DIS QL(*) =WHERE(CURDEPTH GT MAXDEPTH * QDEPTHHI/100)

In fact any arithmetic you need with any selection of object attributes can be put together in an =WHERE clause.


If you don’t already have MQSCX and you’d like to try it out, please email support@mqgem.com to request a trial licence. You can download MQSCX from our website.