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

Apache Log4j CVE-2021-44228 Information

December 14, 2021 by Andrew

Black Hill Software does not use or distribute Apache Log4j in any of our products.

EasySMF:JE does use SLF4J which can be configured by the customer to use Log4j, if the customer provides the Log4j components. Even in this case EasySMF:JE does not log any information from untrusted sources so we do not believe it is vulnerable to this exploit.

However, if customers have configured logging to use Apache Log4j they should upgrade Log4j to a fixed version.

Filed Under: EasySMF News, Java

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

August 10, 2021 by Andrew

Which language is faster on z/OS, Java or C++? People will tell you C++ is fast and Java is slow, but does that stand up to a drag race?

Dave Plummer is a retired operating systems engineer from Microsoft. He has created an interesting series of videos “drag racing” different languages and different hardware with a small program searching for prime numbers. The initial video raced C++, Python, and C#. Then he raced an Apple M1 vs an AMD ThreadRipper 3970X vs a Raspberry Pi.

I thought it would be interesting to run the drag race on z/OS, putting C++ up against Java. z/OS people like to tell you that Java is slow – but is that really true?

The program uses the sieve of Eratosthenes to search for prime numbers. The program works through odd numbers starting at 3 and marks each multiple as “not prime”. Then it moves to the next number that has not already been marked as a multiple of another number and repeats the process. At the end, numbers that have not been marked are prime.

This is repeated for numbers up to 1,000,000 as many times as possible in 5 seconds, and the number of passes is the result.

The “drag race” description acknowledges that this isn’t a comprehensive benchmark, just a test of speed at a particular task like drag racing a car.

Setup

The C++ and Java programs had been developed and refined on other platforms. The Java code ran without modification, but the C++ code required a few changes:

  • I couldn’t find <chrono> on z/OS so I used gettimeofday for the timing
  • Some changes to initialization etc. were required due to unsupported syntax

The C++ code was compiled from the unix command line:

xlc -o PrimeCPP31 -O3 -Wl,xplink -Wc,xplink,-qlanglvl=extended0x PrimeCPP.cpp

I configured the zIIP offline for the tests so that the C++ and Java code were running on the same processor.

All source code is available here, if you want to try it out on your own system:

https://github.com/andrew890/Primes-zOS

Note: z/OS CPU speeds vary widely based on the capacity purchased. The z15 LSPR ratios list z15 systems with single CPU MSU ratings from 12 MSU to 253 MSU – a 20x difference! The numbers here should be a reasonable comparison between the languages tested, but be careful comparing them with a different system.

Round 1

Source code:

  • C++ : https://github.com/andrew890/Primes-zOS/blob/main/PrimeCPP/solution_1/PrimeCPP.cpp
  • Java : https://github.com/andrew890/Primes-zOS/blob/main/PrimeJava/solution_1/PrimeSieveJava.java

Results (higher number is better):

C++Java
12954807

I was surprised – I expected Java to do well, but I didn’t expect C++ to do so badly.

There wasn’t anything I could see in the C++ code to make it slower than the Java code. However, marking and checking numbers is the majority of the work, and this processing is hidden inside a vector<bool> in the C++ code. Using vector<bool> was apparently a big gain on other platforms, but maybe not on z/OS?

I changed the C++ code to use bits in an unsigned char array, explicitly testing and setting bits. This was the method Dave used in his initial code. The Java code used a boolean array. To give the closest possible comparison between C++ and Java I also changed the Java code to use a byte array with the same bit testing/setting.

Round 2

Source Code:

  • C++ : https://github.com/andrew890/Primes-zOS/blob/main/PrimeCPP/solution_2/PrimeCPP.cpp
  • Java : https://github.com/andrew890/Primes-zOS/blob/main/PrimeJava/solution_2/PrimeSieveJava.java

Results (higher number is better):

C++Java
48282715

This was a much better result for C++. It looks like the vector<bool> implementation on z/OS is not as good as other platforms. However in Java the original solution was much better. The improved C++ version didn’t significantly beat the original Java solution.

On other platforms C++ was faster than Java by 40-70%. The versions using the byte array showed a similar margin. I don’t doubt that you could write a C++ version to beat the fastest Java version on z/OS, but I don’t think it would be easy.

Bonus: COBOL

Someone contributed a COBOL version. I tried that out of interest, compiled with OPT(2):

Source Code:

  • https://github.com/andrew890/Primes-zOS/blob/main/PrimeCOBOL/solution_2/PRIMES

Result:

COBOL
2373

Better than the worst C++, but not as good as Java. To be fair, this program is a long way from the type of work COBOL was designed for. I don’t know COBOL well enough to judge if it could be improved.

Scaling it up

The other interesting test is to scale up from 1,000,000 to larger numbers. I repeated the tests using the different solutions for primes up to 10,000,000, 100,000,000 and 1,000,000,000.

The most interesting result here is the Java boolean[] version. This version is as fast as the fastest C++ version for 1,000,000, but the speed declines much faster as the maximum increases. I guess Java is doing some optimizations that don’t work as well for 1 billion element arrays!

The trend was strong enough that it seemed interesting to try a smaller number as well, so I added a 100,000 run. Very interesting – for 100,000, the Java version using the boolean array was more than 20% faster than C++!

100,0001,000,00010,000,000100,000,0001,000,000,000
C++ using vector<bool>14,3771,295122101 in 7.19 seconds
Java using boolean[]64,4234,807271131 in 9.06 seconds
C++ using unsigned char*52,2514,828417282 in 5.14 seconds
Java using byte[]30,4252,715237142 in 6.99 seconds
COBOL19,2702,3738651 in 21.0 seconds

Java Overhead

Java has some overhead starting the Java Virtual Machine. This can be seen in the SMF data.

The SMF data shows the C++ programs had about 4.95 seconds CPU time and 5.02 seconds elapsed time for the 5 second duration measured by the program.

The Java programs had about 5.24 seconds CPU time and 6.16 seconds elapsed. This presumably reflects the overhead of starting the JVM. There was only one CPU online, so any runtime overhead after the program records the start time will be reflected in the score. Java GC etc. threads could not run in parallel on another CPU and accumulate CPU time without slowing the main program. This startup overhead should be less significant for longer running programs.

Conclusion

Java on z/OS is not slow. It can match C++ for speed, to the point where the selection of algorithms and data structures is more important than the language itself. Java deserves to be considered a high performance language on z/OS, as much as C++ or COBOL. There is one caveat: there is significant overhead starting the JVM, so it might not be a good choice for small programs that run very frequently.

Java’s reputation for being slow probably comes from the ease of combining existing components into very large applications, where the programmer may not even be aware of the size of what they have built.

Many z/OS systems have general purpose CPs running less than full speed to reduce software bills. If you have zIIPs running full speed, Java might actually be the fastest language on your system by a fair margin, with the bonus that the Java work probably doesn’t contribute to software costs.

Dave’s Videos

Here are direct links to the first 2 of Dave Plummer’s Software Drag Racing videos:

Filed Under: Java

Java mapping for CICS SMF records

August 3, 2017 by Andrew

The EasySMF Java API now has experimental support for CICS records.

Experimental, because I want to get some feedback from CICS users about class names, usage etc. before locking down the design. In particular:

  • Do the class names and organization make sense to a CICS person? Would other names or a different organization make more sense?
  • Are the examples of how to process data clear and useful?
  • Are there areas where terminology is used incorrectly?

The complete Javadoc is here with an overview of the CICS functionality here.

If you have any comments, you can leave feedback in the comments box below, send it to support@blackhillsoftware.com, or give feedback in person at booth 323 at SHARE in Providence, Rhode Island.

You can try out the API using the 30 day trial available here: 30 Day Trial.

Installation information is available here: EasySMF:JE Java Quickstart

Using the API

EasySMF:JE aims to provide a consistent interface across different SMF record types and sections, and converts values to standard Java types for simple programming.

Dates and Times

Dates and times are converted to java.time classes. Java.time can represent dates and times with a precision of 1 nanosecond.

Times representing a duration e.g. CPU or elapsed time are converted to Duration.
Dates and times of day are converted to LocalDate, LocalTime, LocalDateTime or ZonedDateTime depending on exactly what information is in the field. Typically, times based on UTC(GMT) are converted to ZonedDateTime with ZoneOffset.UTC. Other dates and times are converted to LocalDate/Times.
Java has time zone rules so it is possible to apply a ZoneId to a LocalDateTime and perform date aware conversions between time zones.

Numeric Values

1, 2 and 3 byte integer values and 4 byte signed integer values are converted to int (32 bit signed) values.
4-7 byte integer values and 8 byte signed values are converted to long (64 bit signed).

8 byte unsigned values are available as both long (64 bit signed) and as a BigInteger. The long value may provide better performance if the value will not exceed the maximum value for a long. If a value does exceed the maximum value (i.e. the high order bit is set) an exception will be thrown. If the field value might exceed the maximum value for a long, use the BigInteger version.

Integer values greater than 8 bytes are converted to BigInteger.

Floating point values are converted to Java double.

String Values

EBCDIC and UTF8 string/character values are converted to String. Java uses Unicode internally – values are converted from EBCDIC or UTF8.

Flags

Flag bits within a byte are converted to a boolean value indicating whether the bit is set.

CICS Statistics

Reading CICS statistics is very done the same way as reading sections from other records using the API. Sections of a specific type are returned in a List<E> of that type. If there are no sections of the type in the record an empty List is returned. This allows you to iterate over the sections without explicitly checking whether the sections exist in the record – an empty list will iterate 0 times.

Example

The following code reads all FileControlStatistics sections from type 110 SMF records from the DD INPUT.

 try (SmfRecordReader reader = 
         SmfRecordReader
             .fromDD("INPUT")
             .include(110, Smf110Record.SMFSTSTY))
 {
     for (SmfRecord record : reader)
     {
         Smf100Record r110 = new Smf110Record(record);
         for (FileControlStatistics fc : 
             r110.fileControlStatistics())
         {
             //...   process FileControlStatistics sections here
         }
     }
 }

CICS Performance Monitoring

Accessing data from CICS monitoring performance records is slightly different to other SMF records because the data needs to be accessed using a Dictionary.

Dictionary records are handled automatically, however you cannot access the data from a record before a related dictionary record has been seen. You can check whether a dictionary record is available using Smf110Record.haveDictionary() or simply concatenate all required dictionary records ahead of the data records in the input data.

Specific fields are defined by name and type. Then Performance records are read from the SMF record, and specific fields accessed using getField(…) methods or variations.

Example

 ByteStringField transactionField = ByteStringField.define("DFHTASK","C001");
 TimestampField startField = TimestampField.define("DFHCICS","T005");
 TimestampField stopField = TimestampField.define("DFHCICS","T006");
 ClockField dispatchField = ClockField.define("DFHTASK","S007");

 try (SmfRecordReader reader = 
         SmfRecordReader
             .fromDD("INPUT")
             .include(110, Smf110Record.SMFMNSTY))
 {
     for (SmfRecord record : reader)
     {
         Smf100Record r110 = new Smf110Record(record); 
         if (r110.haveDictionary())
         {
             for (PerformanceRecord perfdata :
                 r110.performanceRecords())
             {
                 String txName = perfdata.getField(transactionField);
                 ZonedDateTime start = perfdata.getField(startField);
                 ZonedDateTime stop = perfdata.getField(stopField);
                 double dispatch = perfdata.getFieldTimerSeconds(dispatchField);

                 //...  process data
             }
         }
     }
 }

Complete CICS Statistics reporting sample

These samples are designed to show how to use the API, not to suggest items that you should specifically be reporting. However comments about their relevance are welcome.

import java.io.*;
import java.util.*;
import static java.util.Comparator.comparing;

import com.blackhillsoftware.smf.*;
import com.blackhillsoftware.smf.cics.*;
import com.blackhillsoftware.smf.cics.statistics.FileControlStatistics;

public class CicsFileStatistics 
{
    public static void main(String[] args) throws IOException 
    {
        Map<String, Map<String, FileData>> applids = 
                new HashMap<String, Map<String, FileData>>();

        try (SmfRecordReader reader = 
                args.length == 0 ? 
                SmfRecordReader.fromDD("INPUT") :
                SmfRecordReader.fromStream(new FileInputStream(args[0]))) 
        {
            reader.include(110, Smf110Record.SMFSTSTY);
            for (SmfRecord record : reader) 
            {
                Smf110Record r110 = new Smf110Record(record);

                Map<String, FileData> applidFiles = 
                        applids.computeIfAbsent(r110.stProductSection().smfstprn(),
                        files -> new HashMap<String, FileData>());

                for (FileControlStatistics fileStats : r110.fileControlStatistics()) 
                {
                    String entryName = fileStats.a17fnam();
                    applidFiles.computeIfAbsent(entryName, 
                            x -> new FileData(entryName)).add(fileStats);
                }
            }
        }
        writeReport(applids);
    }

    private static void writeReport(Map<String, Map<String, FileData>> applidFiles) 
    {

        applidFiles.entrySet().stream()
            .filter(applid -> !applid.getValue().isEmpty())
            .sorted((a, b) -> a.getKey().compareTo(b.getKey()))
            .forEachOrdered(applid -> 
            {
                // Headings
                System.out.format("%n%-8s", applid.getKey());

                System.out.format("%n%-8s %12s %12s %12s %12s %12s %12s %12s %12s%n%n", 
                        "ID", 
                        "Gets", 
                        "Get Upd",
                        "Browse", 
                        "Adds", 
                        "Updates", 
                        "Deletes", 
                        "Data EXCP", 
                        "Index EXCP");

                applid.getValue().entrySet().stream()
                    .map(x -> x.getValue())
                    .sorted(comparing(FileData::getTotalExcps)
                            .reversed())
                    .forEachOrdered(fileInfo -> 
                    {
                        // write detail line
                        System.out.format("%-8s %12d %12d %12d %12d %12d %12d %12d %12d%n", 
                                fileInfo.getId(),
                                fileInfo.getGets(), 
                                fileInfo.getGetUpd(), 
                                fileInfo.getBrowse(),
                                fileInfo.getAdds(), 
                                fileInfo.getUpdates(), 
                                fileInfo.getDeletes(),
                                fileInfo.getDataExcps(), 
                                fileInfo.getIndexExcps());
                    });
                });

    }

    private static class FileData 
    {
        public FileData(String fileId)
        {
            this.id = fileId;
        }

        public void add(FileControlStatistics fileStatistics) 
        {
            gets += fileStatistics.a17dsrd();
            getupd += fileStatistics.a17dsgu();
            browse += fileStatistics.a17dsbr();
            add = fileStatistics.a17dswra();
            update = fileStatistics.a17dswru();
            delete = fileStatistics.a17dsdel();
            dataexcp = fileStatistics.a17dsxcp();
            indexexcp = fileStatistics.a17dsixp();
            totalexcp += fileStatistics.a17dsxcp() 
                    + fileStatistics.a17dsixp();
        }

        public String getId() 
        {
            return id;
        }

        public long getGets() 
        {
            return gets;
        }

        public long getGetUpd() 
        {
            return getupd;
        }

        public long getBrowse() 
        {
            return browse;
        }

        public long getAdds() 
        {
            return add;
        }

        public long getUpdates() 
        {
            return update;
        }

        public long getDeletes() 
        {
            return delete;
        }

        public long getDataExcps() 
        {
            return dataexcp;
        }

        public long getIndexExcps() 
        {
            return indexexcp;
        }

        public long getTotalExcps() 
        {
            return totalexcp;
        }

        private String id;
        private long gets = 0;
        private long getupd = 0;
        private long browse = 0;
        private long add = 0;
        private long update = 0;
        private long delete = 0;
        private long dataexcp = 0;
        private long indexexcp = 0;
        private long totalexcp = 0;
    }
}

Complete CICS Transaction Monitoring reporting sample

import java.io.*;
import java.time.*;
import java.util.*;
import static java.util.Collections.reverseOrder;
import static java.util.Comparator.comparing;

import com.blackhillsoftware.smf.*;
import com.blackhillsoftware.smf.cics.*;
import com.blackhillsoftware.smf.cics.monitoring.*;
import com.blackhillsoftware.smf.cics.monitoring.fields.*;

public class CicsTransactionSummary 
{

    public static void main(String[] args) throws IOException 
    {
        Map<String, Map<String, TransactionData>> applids = 
                new HashMap<String, Map<String, TransactionData>>();

        ByteStringField transaction = ByteStringField.define("DFHTASK", "C001");

        int noDictionary = 0;

        try (SmfRecordReader reader = 
                args.length == 0 ? 
                SmfRecordReader.fromDD("INPUT") :
                SmfRecordReader.fromStream(new FileInputStream(args[0]))) 
        {     
            reader.include(110, Smf110Record.SMFMNSTY);
            for (SmfRecord record : reader) 
            {
                Smf110Record r110 = new Smf110Record(record);

                if (r110.haveDictionary()) 
                {
                    Map<String, TransactionData> applidTransactions = 
                        applids.computeIfAbsent(
                            r110.mnProductSection().smfmnprn(), 
                            transactions -> new HashMap<String, TransactionData>());

                    for (PerformanceRecord mn : r110.performanceRecords()) 
                    {
                        String txName = mn.getField(transaction);
                        applidTransactions.computeIfAbsent(
                                txName, 
                                x -> new TransactionData(txName)).add(mn);
                    }
                } else 
                {
                    noDictionary++;
                }
            }
        }

        writeReport(applids);

        if (noDictionary > 0) 
        {
            System.out.format(
                    "%n%nSkipped %s records because no applicable dictionary was found.", 
                    noDictionary);
        }

    }

    private static void writeReport(Map<String, Map<String, TransactionData>> transactions) 
    {
        transactions.entrySet().stream()
            .sorted((a, b) -> a.getKey().compareTo(b.getKey()))
            .forEachOrdered(applid -> 
            {
                // Headings
                System.out.format("%n%-8s", applid.getKey());

                System.out.format("%n%-4s %15s %15s %15s %15s %15s %15s %15s %15s %15s%n%n", 
                        "Name", 
                        "Count", 
                        "Elapsed",
                        "Avg Elapsed", 
                        "CPU", 
                        "Avg CPU", 
                        "Dispatch", 
                        "Avg Disp.", 
                        "Disp Wait", ""
                                + "Avg Disp Wait");

                applid.getValue().entrySet().stream()
                    .map(x -> x.getValue())
                    .sorted(comparing(TransactionData::getCpu, reverseOrder())
                            .thenComparing(TransactionData::getCount, reverseOrder()))
                    .forEachOrdered(txInfo -> 
                    {
                        // write detail line
                        System.out.format("%-4s %15d %15f %15f %15f %15f %15f %15f %15f %15f%n", 
                                txInfo.getName(),
                                txInfo.getCount(), 
                                txInfo.getElapsed(), 
                                txInfo.getAvgElapsed(), 
                                txInfo.getCpu(),
                                txInfo.getAvgCpu(), 
                                txInfo.getDispatch(), 
                                txInfo.getAvgDispatch(),
                                txInfo.getDispatchWait(), 
                                txInfo.getAvgDispatchWait());

                    });
            });

    }

    private static class TransactionData 
    {
        public TransactionData(String name) 
        {
            this.name = name;
        }

        public void add(PerformanceRecord perfdata) 
        {
            count++;
            elapsed += Utils.ToSeconds(
                    Duration.between(perfdata.getField(start), perfdata.getField(stop)));
            dispatch += perfdata.getFieldTimerSeconds(dispatchField);
            dispatchWait += perfdata.getFieldTimerSeconds(dispatchWaitField);
            cpu += perfdata.getFieldTimerSeconds(cpuField);
        }

        public String getName() 
        {
            return name;
        }

        public int getCount() 
        {
            return count;
        }

        public double getElapsed() 
        {
            return elapsed;
        }

        public double getDispatch() 
        {
            return dispatch;
        }

        public double getDispatchWait() 
        {
            return dispatchWait;
        }

        public double getCpu() 
        {
            return cpu;
        }

        public Double getAvgElapsed() 
        {
            return count != 0 ? elapsed / count : null;
        }

        public Double getAvgDispatch() 
        {
            return count != 0 ? dispatch / count : null;
        }

        public Double getAvgDispatchWait() 
        {
            return count != 0 ? dispatchWait / count : null;
        }

        public Double getAvgCpu() 
        {
            return count != 0 ? cpu / count : null;
        }

        static TimestampField start = TimestampField.define("DFHCICS", "T005");
        static TimestampField stop = TimestampField.define("DFHCICS", "T006");
        static ClockField dispatchField = ClockField.define("DFHTASK", "S007");
        static ClockField dispatchWaitField = ClockField.define("DFHTASK", "S102");
        static ClockField cpuField = ClockField.define("DFHTASK", "S008");

        private String name;
        private int count = 0;
        private double elapsed = 0;
        private double dispatch = 0;
        private double dispatchWait = 0;
        private double cpu = 0;
    }
}

Filed Under: EasySMF News, Java, Java SMF

Sending Email from z/OS using Java

June 21, 2017 by Andrew

How do you send email from your z/OS system?

Java on z/OS makes this very easy. Java on z/OS can use Java libraries developed for other platforms which means there are powerful, free libraries available and plenty of documentation available via Google.

Instead of setting up a SMTP server on z/OS, you can define a user (or more than one) for z/OS in your corporate mail system, and create batch jobs that log in and send mail just like any other email user.

This example uses the JavaMail package (part of Java EE and available for download for Java SE) to send email from z/OS. It uses the JZOS Batch Launcher so you can define the input using JCL DD statements.

You can send an email using JCL like this:

//JAVAG    EXEC PROC=JAVAG,
// JAVACLS='''ZMail''',
// CLASPATH='''java/lib/javax.mail.jar'''
//TO       DD *
someone@example.com
//SUBJECT  DD *
Test Message from batch job
//MESSAGE  DD *
Message line 1
Message line 2
...
Message Line n
//

The JCL uses the JAVAG JCL PROC described in this post: Systems Programmer Friendly Java

The following code is the complete Java program. The sample uses Gmail as an example to show how to use TLS and SMTP authentication. Customize the Properties used to create the Session to suit your own mail server.

To, Subject and the Message Body are read from DD statements defined in the JCL. You could use the same pattern to allow other information to be input from DD statements, e.g. CC, BCC, From, or even the properties used to log in to the mail server.

import java.io.*;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;

import com.ibm.jzos.ZFile;
import com.ibm.jzos.ZFileException;

public class ZMail 
{
    public static void main(String[] args) 
            throws IOException, MessagingException 
    {   
        List<String> messagelines = readLinesFromDD("MESSAGE");
        String subject = readLinesFromDD("SUBJECT").get(0);
        List<String> recipients = readLinesFromDD("TO");        

        StringBuilder messagetext = new StringBuilder();
        for (String line : messagelines)
        {
            messagetext.append(line);
            messagetext.append(System.lineSeparator());
        }

        final String username = "smtp-username";
        final String password = "smtp-password";

        Properties props = new Properties();
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.host", "smtp.gmail.com");
        props.put("mail.smtp.port", "587");
        props.put("mail.ssl.checkserveridentity", "true");

        Session session = Session.getInstance(props,
                new javax.mail.Authenticator() 
                {
                    protected PasswordAuthentication getPasswordAuthentication() 
                    {
                        return new PasswordAuthentication(username, password);
                    }
                });

        Message message = new MimeMessage(session);
        message.setFrom(new InternetAddress("me@example.com"));
        for (String recipient : recipients)
        {
            message.addRecipients(Message.RecipientType.TO, 
                    InternetAddress.parse(recipient));

        }
        message.setSubject(subject);
        message.setText(messagetext.toString());

        Transport.send(message);        
    }

    private static List<String> readLinesFromDD(String dd) 
            throws ZFileException, IOException 
    {
        List<String> lines = new ArrayList<String>();
        ZFile input = null;
        try
        {
            input = new ZFile("//DD:" + dd, "rt");
            BufferedReader reader = 
                new BufferedReader(
                    new InputStreamReader(input.getInputStream()));

            String line;     
            while ((line = reader.readLine()) != null) 
            {   
                lines.add(line);
            }

        }
        finally
        {
            if (input != null)
            {
                input.close();
            }
        }
        return lines;
    }
}

Filed Under: Java

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.

[Read more…]

Filed Under: Java

  • 1
  • 2
  • Next Page »

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