Back to Summer Batch home page

Chapter 7. Using Advanced Features

Table of Contents

Reading and writing EBCDIC files using Cobol copybooks

Legacy Cobol batches involve frequently dealing with EBCDIC files, using copybooks. Modernizing such batches typically requires to be able to read and write those files, given the copybooks. Two classes have been developped to support EBCDIC Read/Write :

  • Summer.Batch.Extra.Ebcdic.EbcdicFileReader : to read from an ebcdic file;

  • Summer.Batch.Extra.Ebcdic.EbcdicFileWriter : to write to an ebcdic file;

Both classes require that a xml version of a Cobol copybook is provided at run time.

Using the EbcdicFileReader

To read records from an ebcdic file, using an EbcdicFileReader, the following elements must be provided :

  • a xml version of the needed Cobol copybook (Copybook property );

  • a class in charge of transforming ebcdic record into a business object (EbcdicReaderMapper property) : this class must implement the Summer.Batch.Extra.Copybook.IEbcdicReaderMapper<T> interface ;

  • a path to the ebcdic file to be read (Resource property).

[Caution]Caution

The two elements (copybook xml file and ebcdic reader mapper class) are mandatory. Failing to provide either of them will prevent the EbcdicFileReader from working, and the job will likely fail.

The Cobol copybook is used by the reader to extract ebcdic records from the ebcdic file.

Example 7.1. Sample xml copybook export

<?xml version="1.0" encoding="ASCII"?>
<FileFormat ConversionTable="IBM037" dataFileImplementation="IBM i or z System" 
	distinguishFieldSize="0" newLineSize="0" headerSize="0">
  <RecordFormat cobolRecordName="BA_EBCDIC_READER" distinguishFieldValue="0">
    <FieldFormat Decimal="0" DependingOn="" ImpliedDecimal="true" Name="CODE" Occurs="1" 
    	Picture="S9(5)" Signed="true" Size="5" Type="3" Value=""/>
    <FieldFormat Decimal="0" DependingOn="" ImpliedDecimal="true" Name="NAME" Occurs="1" 
    	Picture="X(30)" Signed="false" Size="30" Type="X" Value=""/>
    <FieldFormat Decimal="0" DependingOn="" ImpliedDecimal="true" Name="DESCRIPTION" Occurs="1" 
    	Picture="X(40)" Signed="false" Size="40" Type="X" Value=""/>
    <FieldFormat Decimal="0" DependingOn="" ImpliedDecimal="true" Name="DATE" Occurs="1" 
    	Picture="S9(8)" Signed="true" Size="4" Type="B" Value=""/>
  </RecordFormat>
</FileFormat>
				


[Note]Note

A remark about the ConversionTable (=encoding) specified in the copybook xml file: this attribute must be given a property that can be understood by the C# API. This requires to use the C# encoding names (which differ from java encoding names convention -- java uses "Cp037" where C# uses "IBM037" for example). The System.Text.Encoding has the GetEncodings method to list all available encodings.

The xml copybook file must be compliant with the following xsd (full xsd source is given in dedicated appendix section)

Figure 7.1. Ebcdic File Format xml schema

Ebcdic File Format xml schema


The corresponding bound C# classes are located in the Summer.Batch.Extra.Copybook namespace:

  • CopybookElement.cs

  • FieldFormat.cs

  • FieldsGroup.cs

  • FileFormat.cs

  • IFieldsList.cs

  • RecordFormat.cs

Then the ebcdic reader mapper is used to transform those records into more convenient business objects. Below is the business object whose properties will be mapped to the ebcdic record described by the xml copybook above.

Example 7.2. Sample business object to which ebcdic records will be mapped

using System;

namespace Com.Netfective.Bluage.Business.Batch.Ebcdic.Bos
{
    /// <summary>
    /// Entity EbcdicFileBO.
    /// </summary>
    [Serializable]
    public class EbcdicFileBO
    {
        /// <summary>
        /// Property Code.
        /// </summary>
        public int? Code { get; set; }

        /// <summary>
        /// Property Name.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Property Description.
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// Property Date.
        /// </summary>
        public DateTime? Date { get; set; }

    }
}
				


Now, we need to define a mapper in charge of transforming records into business objects. The mapper must implement the Summer.Batch.Extra.Ebcdic.IEbcdicReaderMapper<T> interface. The Summer.Batch.Extra.Ebcdic.AbstractEbcdicReaderMapper<T> is a convenient super class to inherit from in order to craft ebcdic reader mapper quickly (see example below).

Example 7.3. Sample ebcdic reader mapper

using Com.Netfective.Bluage.Business.Batch.Ebcdic.Bos;
using Summer.Batch.Extra.Ebcdic;
using System.Collections.Generic;

namespace Com.Netfective.Bluage.Business.Batch.Ebcdic.Bos.Mappers
{
	///<summary>
	/// Ebcdic mapper for the EbcdicFileBO class.
	///</summary>
    public class EbcdicFileEbcdicMapper : AbstractEbcdicReaderMapper<EbcdicFileBO>
    {
        private const int Code = 0;
        private const int Name = 1;
        private const int Description = 2;
        private const int Date = 3;

		///<summary>
		/// Map a collection of properties to a EbcdicFileBO record. 
		///</summary>
		/// <param name="values"> list of values to be mapped</param>
		/// <param name="itemCount"> item count; will be used as identifier 
		/// if this makes sense for the target class.</param>
		/// <returns>the EbcdicFileBO record build upon the given list of values.</returns>
        public override EbcdicFileBO Map(IList<object> values, int itemCount)
        {
            var record = new EbcdicFileBO
            {
                Code = (int) ((decimal) values[Code]),
                Name = ((string) values[Name]),
                Description = ((string) values[Description]),
                Date = ParseDate(values[Date]),
            };
            return record;
        }
    }
}
				


Then everything needs to be configured. The EbcdicFileReader is declared as any other reader in the job xml file :


And here is the corresponding Unity configuration part :


[Note]Note

A few details are worth mentioning here:

  • The Ebcdic file reader is registered within a step scope, using the StepScopeRegistration extension;

  • The Ebcdic file reader registration is made using the same name it was declared in the job xml file (that is "EbcdicReader/EbcdicFileReader");

  • The

    Property("Resource")
        .Resource("#{settings['BA_EBCDIC_READER.EbcdicReader.FILENAME_IN']}")

    call indicates that the property named "Resource" will be filled with the value read in the Settings.config xml file. Here is the corresponding Settings.config file content :

    <?xml version="1.0" encoding="utf-8" ?>
    <appSettings>
      <add key="BA_EBCDIC_READER.EbcdicReader.FILENAME_IN"
      	value="data\inputs\BA_EBCDIC_READER.data" />
      <add key="BA_EBCDIC_READER.EbcdicReader.COPYBOOK_IN"
      	value="data\copybooks\BA_EBCDIC_READER.fileformat" />
    </appSettings>

To write records to an ebcdic file, using an EbcdicFileWriter, the following elements must be provided:

  • a list of Cobol copybooks xml versions (Copybooks property);

  • a class in charge of transforming ebcdic record into a business object (EbcdicWriterMapper property): one can use the Summer.Batch.Extra.Ebcdic.EbcdicWriterMapper class or a custom sub-class that inherits from it;

  • a path to the ebcdic file to be written (Resource property).

[Caution]Caution

The writer takes a list of xml copybooks but only uses one copybook at a time; the writer has a convenient method (ChangeCopyBook) to change the current copybook being used.

The three elements (copybook xml files list, ebcdic writer mapper class and path to resource to write to) are mandatory. Failing to provide either of them will prevent the EbcdicFileWriter from working, and the job will likely fail.

Let's review the corresponding needed configuration. The writer must be declared in the job xml file (as any other writer) :


Regarding Unity configuration, here is the corresponding part:


[Note]Note
  • The Ebcdic file writer is registered within a step scope, using the StepScopeRegistration extension;

  • The Ebcdic file writer registration is made using the same name it was declared in the job xml file (that is "EBCDIC_WRITER/EbcdicFileWriter");

  • Regarding the list of copybooks resources loading :

    .Property("Copybooks").Resources("#{settings[…]}")

    The .Resources() call (note the extra "s") is able to load into a list of resource paths a semicolon-separated path string.

  • The Summer.Batch.Extra.Ebcdic.EbcdicWriterMapper is able to automatically convert a business object into a list of values that can be written in an ebcdic record (see the Summer.Batch.Extra.Ebcdic.EbcdicWriterMapper#Map method). The automatic mapping between the business object properties and the copybook records is made on a name convention basis. The property name must be equal to the FieldFormat Name attribute, converted to camel case.

    e.g.

    <FieldFormat Decimal="0" DependingOn="" ImpliedDecimal="true"
        Name="DESCRIPTION" Occurs="1" Picture="X(40)" Signed="false"
        Size="40" Type="X" Value=""/>

    will automatically map to the property named "Description" (= camel case version of "DESCRIPTION")

    /// <summary>
    /// Property Description.
    /// </summary>
    public string Description { get; set; }

    To make sure you are using the correct names, in order to guarantee the automatic mapping between records and business object properties, you can use the ToCamelCase method from the Summer.Batch.Extra.Ebcdic.AbstractEbcdicMapper class;

Back to Summer Batch home page