Black Hill Software

  • Home
  • Products
    • EasySMF
      • Release Notes and Latest Version
      • Online Manual
      • License Agreement
    • EasySMF:JE
      • EasySMF:JE Java Quickstart
      • Release Notes and Latest Version
      • Javadoc
      • EasySMF JSON Javadoc
      • License Agreement
    • 30 Day Trial
  • Purchase
    • How to Buy
    • Purchase a License
  • Support
    • EasySMF Support
    • Get the latest version of EasySMF
    • EasySMF:JE Support
    • Get the latest version of EasySMF:JE
  • News
  • Contact
    • Support
    • Sales

z/OS Productivity Tools using Java

August 11, 2015 by Andrew

This article examines how Java can be a powerful productivity tool for a Systems Programmer, using the task of synchronizing Master Catalogs as an example.

Systems programmers are often dismissive of Java. They think it is memory hungry, has poor performance and is not useful for real systems programming work.

In fact, the reality is that Java performance on z/OS is good, and being improved all the time.

More importantly, it is a powerful language and when combined with the functions provided by the JZOS Toolkit can be very useful for writing System Programmer utilities.

Example: Synchronizing Master Catalogs

Comparing or synchronizing master catalogs is a common task. You may have several systems with separate master catalogs, or need to merge a master catalog from a Serverpac installation with a live master catalog.

This Java example generates IDCAMS commands to synchronize catalogs. It could be adapted to define new entries only, work with specific HLQs etc.

The power of Java Collections

Java includes a Collections framework. As its name implies, the Collections framework provides functions for working with collections of data – Lists, Maps, Sets etc.

One of the most useful Collections classes is the Map, which maps keys to values. I usually use the HashMap specialization, which uses a hash table to map the values and generally has excellent performance.

Using a Map, the logic for comparing catalogs is simple:

  1. Add entries from the first catalog to a map using the name as a key
  2. Get all entries from the second catalog and:
    1. If there is a matching name in the map – remove the entry from the map and compare the attributes.
    2. Otherwise, record that the entry is in the second catalog only.
  3. The remaining entries in the map are in the first catalog only.

Sample code showing the logic:

Map<String, CatalogEntry> cat1Entries 
    = new HashMap<String, CatalogEntry>(); 
for (CatalogEntry entry : 
     getCatalogEntries(catalog1, "**", null)) 
{ 
    cat1Entries.put(entry.entryName, entry);
} 

List<CatalogEntry> cat2Only 
    = new ArrayList<CatalogEntry>(); 

for (CatalogEntry cat2Entry : 
     getCatalogEntries(catalog2, "**", null))
{             
    CatalogEntry cat1Entry 
        = cat1Entries.remove(cat2Entry.entryName); 
    if (cat1Entry != null) // found entry from catalog1
    {          
        if (!cat2Entry.equals(cat1Entry)) 
        {   
            // same name, different attributes
        }
        // else entries match 
    }   
    else  
    {   
       // entry is in catalog 2 only
    }  
}  
// Remaining entries in map are in catalog 1 only
List<CatalogEntry> cat1Only 
    = new ArrayList<CatalogEntry>(
             cat1Entries.values());

Catalog comparison logic

CatalogSync Implementation

The program consists of a CatalogEntry class, which provides the functions to work with catalog entries, and the main program with the comparison logic.

CatalogEntry Class

CatalogEntry instances are created from entries returned from the JZOS CatalogSearch function. The CatalogEntry constructor takes the JZOS CatalogSearch.Entry and extracts the information we are interested in.

import java.util.*; 
import com.ibm.jzos.CatalogSearch;  

public class CatalogEntry { 
    public CatalogEntry(CatalogSearch.Entry entry)
    {    
        entryName = 
           entry.getField("ENTNAME").getFString();
        entryType = 
           entry.getField("ENTYPE").getFString(); 
        relatedNames = 
           entry.getField("NAME").getFStringArray(44);
        if (relatedNames == null)
           relatedNames = new String[] {}; 
        volumes = 
           entry.getField("VOLSER").getFStringArray(6);
        if (volumes == null) 
           volumes = new String[] {}; 
        deviceTypes = 
           entry.getField("DEVTYP").getIntArray(4);
        if (deviceTypes == null) 
           deviceTypes = new int[] {};
    } 

    public String entryName;
    public String entryType; 
    public String[] relatedNames; 
    public String[] volumes;
    public int[] deviceTypes; 
}

CatalogEntry.java class with constructor

The NAME, VOLSER and DEVTYP from CatalogSearch.Entry can be null if the attribute does not exist for that entry. In that case they are set to empty arrays, because that simplifies comparison processing.

Our CatalogEntry class has the following additional methods:

CatalogEntry.getCatalogEntries

getCatalogEntries reads entries from the specified catalog.

It is a static method which is used by the main program to retrieve the list of entries.

getCatalogEntries uses the JZOS CatalogSearch class which provides a Java interface to the z/OS Catalog Search Interface (CSI).

    public static List<CatalogEntry> getCatalogEntries(
            String catalogname, 
            String search, 
            String entryTypes)
    {  
        List<CatalogEntry> result = 
           new ArrayList<CatalogEntry>(); 
        CatalogSearch catSearch = 
           new CatalogSearch(search);
        catSearch.setCatalogName(catalogname);
        catSearch.setEntryTypes(entryTypes); 
        catSearch.setCatalogName(catalogname); 
        catSearch.setSingleCatalog(true); 
        catSearch.addFieldName("ENTYPE");
        catSearch.addFieldName("ENTNAME");
        catSearch.addFieldName("NAME");
        catSearch.addFieldName("DEVTYP"); 
        catSearch.addFieldName("VOLSER");
        catSearch.search(); 
        while (catSearch.hasNext())  
        {  
            CatalogSearch.Entry entry = 
               (CatalogSearch.Entry)catSearch.next();
            if (entry.isDatasetEntry())
            { 
                result.add(new CatalogEntry(entry));
            }    
        } 
        return result;
    }

CatalogEntry.getCatalogEntries method

getCatalogEntries sets various CSI parameters then invokes the search, returning the result as a List.

Note: The line catSearch.setSingleCatalog(true) says that only the specified catalog will be searched. Otherwise entries from multiple catalogs (e.g. the current master catalog) can be returned. This gives false results when trying to compare 2 catalogs.

For more information on the CatalogSearch function refer to the JZOS CatalogSearch documentation, and the Catalog Search Interface documentation in the z/OS Managing Catalogs manual.

CatalogEntry.equals and CatalogEntry.hashcode

CatalogEntry.equals tests whether 2 catalog entries are equal.

CatalogEntry.hashcode is not used in this program, but is included because when you override the equals function it is good practice to also override the hashcode function. Equals and hashcode are used when the class is used as a key in a HashMap. The rule is that if 2 instances are equal they must have the same hashcode.

This class simply returns the hashcode from the entry name as the CatalogEntry.hashcode.1

    @Override 
    public boolean equals(Object o) 
    {  
        if (o == this) return true; // same object
        if (!(o instanceof CatalogEntry)) return false;
        CatalogEntry ce = (CatalogEntry) o; 
        return (Objects.equals(
                   this.entryName, ce.entryName)
                && Objects.equals(
                   this.entryType, ce.entryType)
                && Arrays.equals(
                   this.relatedNames, ce.relatedNames)
                && Arrays.equals(
                   this.volumes, ce.volumes) 
                && Arrays.equals(
                   this.deviceTypes, ce.deviceTypes)
                ); 
    }

    @Override 
    public int hashCode()
    { 
        return entryName.hashCode();
    }

CatalogEntry.equals and hashcode methods

CatalogEntry.deviceType

This is a utility function to translate the device type from the catalog to a device type for the IDCAMS command.

     String deviceType(int devtype)
     {
        switch (devtype)
        {
           case 0:
               return "0000";  
           case 0x3010200E:   
               return "3380";  
           case 0x3010200F:
               return "3390";   
           default:
              return String.format(
                "Device type %8X not implemented", 
                devtype);
        }  
     }

CatalogEntry. deviceType method

CatalogEntry.deleteCommand, defineCommand

The deleteCommand and defineCommand methods generate the IDCAMS commands used to synchronize the catalogs.

The methods use String formatting to insert the values into the commands.

     public String deleteCommand(String catalog)
     { 
        switch (entryType)  
        {  
           case "A": //NONVSAM  
              return String.format(
                 " DELETE -%n" +  
                 "    %s -%n" + 
                 "    NOSCRATCH -%n"+ 
                 "    CATALOG(%s)%n", 
                 entryName, 
                 catalog);
           case "X": //ALIAS    
              return String.format( 
                 " DELETE -%n" +  
                 "    %s -%n" + 
                 "    ALIAS -%n"+ 
                 "    CATALOG(%s)%n", 
                 entryName,  
                 catalog); 
           default: 
              return String.format(
                 "Delete entry type %s " +
                 "not implemented, %s%n", 
                 entryType, entryName); 
        }  
     }

CatalogEntry.deleteCommand method

     public String defineCommand(String catalog)
     {
        switch (entryType)
        {
           case "A": //NONVSAM
              String volList = volumes[0];
              for (int i=1; i < volumes.length; i++)
              {
                 volList += " " + volumes[i];
              } 
              String devList =
                 deviceType(deviceTypes[0]);
              for (int i=1;
                   i < deviceTypes.length; i++)
              {
                 devList +=
                    " " + deviceType(deviceTypes[i]);
              }
              return String.format(
                 " DEFINE NONVSAM -%n" +
                 "    (NAME(%s) -%n" +
                 "    DEVICETYPES(%s) -%n"+
                 "    VOLUMES(%s) )-%n"+ 
                 "    CATALOG(%s)%n",
                 entryName,
                 devList,
                 volList,
                 catalog);
           case "X": //ALIAS
              return String.format(
                 " DEFINE ALIAS -%n" +
                 "    (NAME(%s) -%n" +
                 "    RELATE(%s) ) -%n" +
                 "    CATALOG(%s)%n",
                 entryName,
                 relatedNames[0],
                 catalog);
           default:
              return String.format(
                 "Define entry type %s " +
                 "not implemented, %s%n",
                 entryType, entryName);
        }
     }

CatalogEntry.defineCommand method

defineCommand handles the case where there are multiple volumes in the entry by building strings with the volumes and device types before inserting them into the command.

The CatalogSync program

This is the main program to generate the catalog synchronization commands.


DANGER

Automatically generated commands can severely damage your system if they contain errors. Before running the commands generated by this program, please make sure that:

  • The generated commands are correct, and operating on the correct catalogs.
  • All required continuation characters are present so that the statement specifying the target catalog is part of the statement.
  • You understand what the commands are doing.
  • You have a plan for recovery if an error occurs.

The catalog names are passed as arguments to the program, and the delete/define commands are written to the DD COMMANDS.

This program only deals with NONVSAM and ALIAS entries, so the call to getCatalogEntries specifies “AX” as the entry types. (See the CSI documentation in the IBM Managing Catalogs manual.)

import java.io.*;
import java.util.*;
import com.ibm.jzos.*;

public class CatalogSync
{
    public static void main(String[] args) 
          throws IOException
    {
        if (args.length != 2)
        {
            System.out.println(
               "Usage: CatalogSync catalog1 catalog2");
            return;
        }
        String cat1 = args[0];
        String cat2 = args[1];

        try (PrintWriter writer 
           = new PrintWriter(
                 FileFactory.newOutputStream(
                    "//DD:COMMANDS")))
        {
            Map<String, CatalogEntry> cat1Entries 
                = new HashMap<String, CatalogEntry>();
            for (CatalogEntry entry : 
                CatalogEntry.getCatalogEntries(
                   cat1, "**", "AX"))
            {
                cat1Entries.put(
                   entry.entryName, entry);
            }

            for (CatalogEntry cat2entry : 
                CatalogEntry.getCatalogEntries(
                   cat2, "**", "AX"))
            {
                CatalogEntry cat1entry = cat1Entries
                        .remove(cat2entry.entryName);
                if (cat1entry != null)
                {
                    if (!cat2entry.equals(cat1entry))
                    {
                        writer.print(
                           cat2entry.deleteCommand(
                              cat2));
                        writer.print(
                           cat1entry.defineCommand(
                              cat2));
                    }
                    // else entries match
                } else
                {
                    writer.print(
                       cat2entry.deleteCommand(
                          cat2));
                }
            }

            List<CatalogEntry> cat1Only 
               = new ArrayList<CatalogEntry>(
                     cat1Entries.values());
            Collections.sort(cat1Only, 
               new Comparator<CatalogEntry>()
               {
                  public int compare(
                     CatalogEntry s1, CatalogEntry s2)
                     {
                        return                             
                           s1.entryName.compareTo(
                              s2.entryName);
                      }
                });

            for (CatalogEntry entry : cat1Only)
            {                
               writer.print(
                  entry.defineCommand(cat2));
            }
        }
    }
}

CatalogSync.java class

Compiling and Running the Program

This JCL uses the PROCs described in the article Systems Programmer Friendly Java to compile and run the complete program:

//JAVAG    EXEC PROC=JAVA8UCG,
// JAVACLS='CatalogSync'
//G.MAINARGS DD * 
CATALOG.MASTER
CATALOG.MASTER.COPY
//G.COMMANDS DD DISP=SHR,DSN=USERID.JCL.CNTL(GENCMDS)

Compile and Execution JCL

Conclusion

Java is a powerful language and a useful addition to z/OS. Hopefully this article has given you some ideas about how it could make your job easier.


1: Hash collisions can cause performance problems when using hash tables, so it is recommended that all the members that are used to determine equality are used when calculating a hashcode. In this case I am using the lazy assumption that CatalogEntries used as keys in a hashtable will normally have different entry names.

Filed Under: Java

30 Day Trial

EasySMF and EasySMF:JE are available for a free 30 day trial. Download now and start using them immediately.
30 Day Trial

Information

EasySMF:JE Java API for SMF Quickstart

EasySMF:JE Sample 1 : SMF Records by type and subtype

Systems Programmer Friendly Java

Sending Email from z/OS using Java

Sign up for EasySMF News

Stay up to date. Enter your email address to receive updates about EasySMF.
unsubscribe from list

Black Hill Software

Suite 10b, 28 University Drive, Mt Helen, VIC 3350, Australia
PO Box 2214, Bakery Hill, VIC 3354, Australia
+61 3 5331 8201
+1 (310) 634 9882
info@blackhillsoftware.com

News

  • Finding UID 0 work on z/OS using SMF Data
  • Apache Log4j CVE-2021-44228 Information
  • Java vs C++ : Drag Racing on z/OS

Twitter

My Tweets

Copyright © 2023 · Enterprise Pro Theme on Genesis Framework · WordPress · Log in