All Things Architecture Blog

Integrating Between COBOL and Java: Dealing with Variable Text Fields

UPDATE: There are certain conditions where converting to a Java String after marshalling causes some comp and comp-3 fields to produce a different number. For example, a COMP 21 marshalled in Java becomes a COMP 31 in COBOL. To avoid this, always use a byte[] instead, and map it to a VARBINARY column in the database.

I know this particular blog post deviates a bit from pure architecture, but from an integration architecture standpoint I thought this was valuable information to share. 

There are several ways to communicate with COBOL from Java. For example, you can use SOAP Web Services or invoke COBOL from a DB2 Stored Procedure (I'll save that for another blog post). Whatever the method, there may be times when you are faced with the dreaded variable text fields. 

Variable text fields allow you to redefine a general text field that can take many forms. For example:

01 WLI-VARIABLE-TEXT        PIC X(300).

01 WSH-HEADING-ONE REDEFINES WLI-VARIABLE-TEXT.                                       

   05 FIELD-ONE             PIC X(30).

   05 FIELD-TWO             PIC 9(9).   

   05 FIELD-THREE           PIC S9(9)V99 COMP-3.

   05 FIELD-FOUR            PIC S9(4) COMP.

FIELD_ONE and FIELD-TWO are not a problem because these are standard non-packed EBCDIC to ASCIII conversions that the platform handles. However, FIELD-THREE and FIELD-FOUR are packed EBCDIC fields, that, when sent back to Java as Strings, contain packed EBCDIC values that Java is not able to read. Note that this is only an issue with variable text in the LINKAGE-SECTION of the COBOL program because COBOL is expecting (or returning) a text field only. 

To handle this situation, you will need to use IBM utilities to marshall and unmarshall the COBOL text into the corresponding numbers. The issue is, there is little or no documentation on how to do this. After a lot of painstaking testing back and forth with most data types, I have created a utility that you can use to ease your integration pain. Just copy-paste these methods into a utility class you can create yourself, and off you go!

Note that these marshallers and unmarshallers will require the marshall.jar file, which is shipped with WebSphere Application Server.

The following table will help guide you in terms of which method to call for each COBOL field PICTURE: 



For each of the methods below, if the method takes a length field, it should be the number of characters in the picture clause. For example, S9(9)V99 COMP-3 would be a lenth of 11; S9(4) COMP would be a lenth of 4. The methods will calculate the packed size based on the length.


public static byte[] marshallCompFields(Long compInput, int length)
     throws UnsupportedEncodingException {

     int packedSize = (length < 5) ? 2 : (length < 10) ? 4 : 8;

     byte[] compBytes = new byte[packedSize];

     switch (packedSize) {

          case 2: MarshallIntegerUtils.marshallTwoByteIntegerIntoBuffer(compInput.shortValue(), 

                         compBytes, 0, true,         

                         MarshallIntegerUtils.SIGN_CODING_TWOS_COMPLEMENT); break;

        case 4: MarshallIntegerUtils.marshallFourByteIntegerIntoBuffer(compInput.intValue(),

                      compBytes, 0, true,

                      MarshallIntegerUtils.SIGN_CODING_TWOS_COMPLEMENT); break;

case 8: MarshallIntegerUtils.marshallEightByteIntegerIntoBuffer(compInput, compBytes, 0,           

              true, MarshallIntegerUtils.SIGN_CODING_TWOS_COMPLEMENT); break;

}

return compBytes;

}


public static byte[] marshallComp3NonDecimalFields(BigDecimal comp3Input, int length) throws UnsupportedEncodingException {

    int packedSize = (length % 2 == 0) ? (length+2)/2 : (length+1)/2;

    byte[] comp3Bytes = new byte[packedSize];

    MarshallPackedDecimalUtils.marshallPackedDecimalIntoBuffer(comp3Input, comp3Bytes, 

        0, packedSize, true,

        MarshallExternalDecimalUtils.EXTERNAL_DECIMAL_SIGN_EBCDIC);

    return comp3Bytes;

}


public static byte[] marshallComp3DecimalFields(BigDecimal comp3Input, int length) throws UnsupportedEncodingException {

    int packedSize = (length % 2 == 0) ? (length+2)/2 : (length+1)/2;

    byte[] comp3Bytes = new byte[packedSize];

    MarshallPackedDecimalUtils.marshallPackedDecimalIntoBuffer(comp3Input.multiply(new   

        BigDecimal(100)), comp3Bytes, 0, packedSize, true,

    MarshallExternalDecimalUtils.EXTERNAL_DECIMAL_SIGN_EBCDIC);

    return comp3Bytes;

}


public static byte[] marshallSignedDecimalFields(BigDecimal signedInput, int length) throws UnsupportedEncodingException {

    byte[] signedBytes = new byte[length];

    MarshallExternalDecimalUtils.marshallExternalDecimalIntoBuffer(signedInput, 

        signedBytes, 0, length, true,

        2, MarshallExternalDecimalUtils.SIGN_FORMAT_TRAILING,   

        MarshallExternalDecimalUtils.EXTERNAL_DECIMAL_SIGN_EBCDIC);

    return signedBytes;

}


public static byte[] marshallSignedIntegerFields(Integer signedInput, int length) throws UnsupportedEncodingException {

byte[] signedBytes = new byte[length];

   MarshallExternalDecimalUtils.marshallExternalDecimalIntoBuffer(signedInput.intValue(), signedBytes, 0, length, true,

   MarshallExternalDecimalUtils.SIGN_FORMAT_TRAILING, MarshallExternalDecimalUtils.EXTERNAL_DECIMAL_SIGN_EBCDIC);

return signedBytes;

}


public static Short unmarshallSignedCompFields(String compInput, int length) throws UnsupportedEncodingException {

    int packedSize = (length < 5) ? 2 : (length < 10) ? 4 : 8;

    byte[] recordByte = compInput.getBytes("Cp037");

    return MarshallExternalDecimalUtils.unmarshallShortFromBuffer(recordByte, 0, 

        packedSize, false, -1,

        MarshallExternalDecimalUtils.EXTERNAL_DECIMAL_SIGN_EBCDIC);

}


public static Short unmarshallUnsignedCompFields(String compInput, int length) throws UnsupportedEncodingException {

     byte[] recordByte = compInput.getBytes("Cp037");

    return MarshallIntegerUtils.unmarshallTwoByteIntegerFromBuffer(recordByte, 0, true,  

        MarshallIntegerUtils.SIGN_CODING_UNSIGNED_BINARY);

}


public static BigDecimal unmarshallComp3NonDecimalFields(String comp3Input, int length) throws UnsupportedEncodingException {

    int packedSize = (length % 2 == 0) ? (length+2)/2 : (length+1)/2;

    byte[] recordByte = comp3Input.getBytes("Cp037");

    return new 

        BigDecimal(MarshallPackedDecimalUtils.unmarshallDoubleFromBuffer(recordByte, 0, 

        packedSize,

    MarshallExternalDecimalUtils.EXTERNAL_DECIMAL_SIGN_EBCDIC));

}


public static BigDecimal unmarshallComp3DecimalFields(String comp3Input, int length) throws UnsupportedEncodingException {

    int packedSize = (length % 2 == 0) ? (length+2)/2 : (length+1)/2;

    byte[] recordByte = comp3Input.getBytes("Cp037");

    return new 

        BigDecimal(MarshallPackedDecimalUtils.unmarshallDoubleFromBuffer(recordByte, 0, 

        packedSize,

        MarshallExternalDecimalUtils.EXTERNAL_DECIMAL_SIGN_EBCDIC)/100).setScale(2,  

        RoundingMode.HALF_DOWN);

}


public static BigDecimal unmarshallSignedDecimalFields(String signedInput, int length) throws UnsupportedEncodingException {

    byte[] recordByte = signedInput.getBytes("Cp037");

    return MarshallExternalDecimalUtils.unmarshallBigDecimalFromBuffer(recordByte, 0, 

        length, true, 2,

        MarshallExternalDecimalUtils.SIGN_FORMAT_TRAILING,

        MarshallExternalDecimalUtils.EXTERNAL_DECIMAL_SIGN_EBCDIC);

}


public static BigDecimal unmarshallSignedLongFields(String signedInput, int length) throws UnsupportedEncodingException {

    byte[] recordByte = signedInput.getBytes("Cp037");

    return new BigDecimal(MarshallExternalDecimalUtils.unmarshallIntFromBuffer(recordByte, 

        0, length, true,

        MarshallExternalDecimalUtils.SIGN_FORMAT_TRAILING,

        MarshallExternalDecimalUtils.EXTERNAL_DECIMAL_SIGN_EBCDIC));

}




© Mark Richards 2016