Black Hill Software

  • Home
  • Products
    • EasySMF
      • Online Manual
      • Release Notes and Latest Version
      • License Agreement
    • EasySMF:JE
      • EasySMF:JE Java Quickstart
      • Release Notes and Latest Version
      • Javadoc
      • License Agreement
    • EasySMF:RTI
      • EasySMF:RTI – SMF Real Time Interface
      • Javadoc
    • 30 Day Trial
  • Purchase
    • How to Buy
    • Purchase a License
  • Support
    • Documentation
      • EasySMF Desktop Online Manual
      • EasySMF:JE Javadoc
      • EasySMF RTI Javadoc
      • EasySMF JSON Javadoc
      • z/OS Utilities Javadoc
    • EasySMF Support
    • Get the latest version of EasySMF
    • EasySMF:JE Support
    • Get the latest version of EasySMF:JE
  • News
  • Contact
    • Support
    • Sales

Java SMF Sample 1 : SMF Records by type and subtype

November 4, 2014 by Andrew

This sample program reads all the SMF records from a file or dataset, and for each record type and subtype lists the number of records, total bytes and percentage of the total.

These examples are not intended to suggest any particular coding style or structure for Java programs. They are intended to show what is possible and illustrate how to access various features of the Java SMF API. In fact I have deliberately ignored some Java conventions in these samples (e.g. encapsulation in the inner classes) to cut the total amount of code and concentrate on illustrating the SMF processing facilities.

To start with, I’ll skip the most of the Java stuff and concentrate on the SMF processing.

Reading SMF data

The SmfRecordReader class provides the facility to read SMF records. It uses a JZOS RecordReader internally to read from a DDNAME or reads directly from a stream.

A stream can be any type of InputStream. Typically it would be a file but it could also be some sort of network stream, or you could chain streams together to read compressed data etc. The stream must contain the record descriptor words (RDWs) so the record lengths can be determined.

SmfRecordReader implements AutoCloseable which means that it can be used in a try-with-resources block to automatically close the reader when exiting the block.

To open a DD for reading:

try (SmfRecordReader reader = SmfRecordReader.fromDD("INPUT")) {
    ...
}

To read from a file:

try (SmfRecordReader reader = SmfRecordReader
        .fromStream(new FileInputStream(
            "C:\\Users\\Andrew\\Documents\\SMF Data\\weekly.smf"))) {
    ...
}               

The samples handle both cases by accepting the filename as an argument to the program. If no filename is provided they open the INPUT DD:

try (SmfRecordReader reader = 
        args.length == 0 ?
        SmfRecordReader.fromDD("INPUT") :
        SmfRecordReader.fromStream(new FileInputStream(args[0])))                
{ 
    ...
}

This makes it simple to develop and test on the workstation, then move the program to z/OS to run as a batch job.

Processing the data

for (SmfRecord record : reader) 
{        
   // do stuff with record...
};

SmfRecordReader implements the Iterable<SmfRecord> interface. The loop above will read and process each record until the reader indicates that no more records are available. Each iteration provides the next SmfRecord in the record variable.

Accessing information from the record

Record information can be accessed using record methods:

Integer key = (record.recordType() << 16) 
    + (record.hasSubtypes() ? record.subType() : 0);
int length = record.recordLength();

That’s all the SMF processing this program does.

The Java Stuff

You could probably store record type and subtype information in a very sparse 2 dimensional array, but for reduced memory usage this program uses a HashMap with the type and subtype combined into an Integer key, and an object containing the statistics as the value.

Map<Integer, RecordStats> statsMap = 
    new HashMap<Integer, RecordStats>();

RecordStats is the class that keeps the counters for a type/subtype combination.

The processing is simple:

For each record:
1. Get the type/subtype key
2. Try to get an existing statistics entry for the key
3. If there is no existing entry, create a new RecordStats entry

Integer key = (record.recordType() << 16) 
    + (record.hasSubtypes() ? record.subType() : 0);

RecordStats stats = statsMap.get(key);
if (stats == null) 
{
    statsMap.put(key, new RecordStats(record));
}
else 
{
    stats.add(record);
}

SMF record type is a 1 byte field and subtype is 2 bytes, so they can both be combined into a 4 byte Integer to form the key.

Output

The writeReport method produces the output.

The statistics entries are sorted by the largest to smallest number of bytes. First the map entries need to be put into a List for sorting:

List<Entry<Integer, RecordStats>> results = 
    new ArrayList<Entry<Integer, RecordStats>>(statsMap.entrySet());

Then the Collections.sort method is used to sort the list. We can provide a Comparator in the call to sort to sort by anything we like. In this case we compare the bytes in the RecordStats object.

Collections.sort(results, 
    new Comparator<Entry<Integer, RecordStats>>()
    {
        public int compare(
            Entry<Integer, RecordStats> s1, 
            Entry<Integer, RecordStats> s2) 
            {
                return Long.compare( // reversed to sort descending
                    s2.getValue().bytes, 
                    s1.getValue().bytes);
            }
    });

Then System.out.format is used to write headings and data. The output:

Type  Subtype     Records          MB     Pct       Min       Max       Avg 
   74        5       60384        1330    23.5      4668     32484     23107 
   30        4      857136        1204    21.3      1137     32740      1473 
   30        3      853873        1195    21.1       626     32740      1468 
   74        1       37536        1128    19.9     17668     32632     31517 
   30        5       97123         236     4.2      1137     32740      2558 
   ...

The complete Java program:

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.blackhillsoftware.smf.SmfRecord;
import com.blackhillsoftware.smf.SmfRecordReader;

public class recordcount 
{
    public static void main( String[] args ) throws IOException
    {
        // use a HashMap to keep the statistics
        Map<Integer, RecordStats> statsMap = 
                new HashMap<Integer, RecordStats>();

        // Read from a DD or file
        try (SmfRecordReader reader = 
                args.length == 0 ?
                SmfRecordReader.fromDD("INPUT") :
                SmfRecordReader.fromStream(new FileInputStream(args[0])))
        { 

            // SmfRecordReader implements Iterable<SmfRecord>, so we can 
            // simply iterate to read SmfRecords
            for (SmfRecord record : reader) 
            {        
                Integer key = (record.recordType() << 16) 
                        + (record.hasSubtypes() ? record.subType() : 0);

                // try to retrieve an existing entry
                RecordStats stats = statsMap.get(key);
                if (stats == null) // none -> create new
                {
                    statsMap.put(key, new RecordStats(record));
                }
                else // otherwise add this record to existing
                {
                    stats.add(record);
                }
            };
        }
        writeReport(statsMap);
    }

    private static void writeReport(Map<Integer, RecordStats> statsMap) 
    {
        // Turn the HashMap into a list so it can be sorted
        List<Entry<Integer, RecordStats>> results = 
                new ArrayList<Entry<Integer, RecordStats>>(statsMap.entrySet());

        // get the total bytes from all record types
        long totalbytes = 0;
        for (Entry<Integer, RecordStats> entry : results)
        {
            totalbytes += entry.getValue().bytes;
        }

        // sort by total bytes descending
        Collections.sort(results, 
                new Comparator<Entry<Integer, RecordStats>>()
                {
                    public int compare(
                            Entry<Integer, RecordStats> s1, 
                            Entry<Integer, RecordStats> s2) 
                    {
                        return Long.compare( // reversed to sort descending 
                                        s2.getValue().bytes, 
                                        s1.getValue().bytes);
                    }
                }
                );

        // write heading
        System.out.format("%5s %8s %11s %11s %7s %9s %9s %9s%n",
                "Type",
                "Subtype",
                "Records",
                "MB",
                "Pct",
                "Min",
                "Max",
                "Avg");

        // write data 
        for (Entry<Integer, RecordStats> entry : results)
        {
            System.out.format("%5d %8d %11d %11d %7.1f %9d% 9d% 9d%n",
                    entry.getKey().intValue() >> 16,
                    entry.getKey().intValue() & 0xFFFF,
                    entry.getValue().count,
                    entry.getValue().bytes / (1024*1024),
                    (float)(entry.getValue().bytes) / totalbytes * 100,
                    entry.getValue().min,
                    entry.getValue().max,
                    entry.getValue().bytes / entry.getValue().count);
        }
    }

    /**
     * Statistics for a type/subtype combination
     */
    private static class RecordStats
    {
        public RecordStats(SmfRecord record)
        {
            count = 1;
            bytes = min = max = record.recordLength();
        }    

        int count;
        long bytes;
        int max;
        int min;

        public void add(SmfRecord record) 
        {
            count++;
            int length = record.recordLength();
            bytes += length;
            if (length < min)
                min = length;
            if (length > max)
                max = length;
        }
    }
}

Filed Under: Java SMF

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

Java vs C++ : Drag Racing on z/OS

News

  • Using zEDC compression for SMF data
  • Text message alerts using the z/OS SMF Real Time Interface
  • DCOLLECT Reports and DCOLLECT to JSON using Java

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

  • Using zEDC compression for SMF data
  • Text message alerts using the z/OS SMF Real Time Interface
  • DCOLLECT Reports and DCOLLECT to JSON using Java

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