COBOL

COBOL

COBOL

COBOL is responsible for the efficient, reliable, secure, and unseen day-to-day operations of the world's economy.

 View Only
  • 1.  Calling Java from Enterprise COBOL program

    Posted Tue May 23, 2017 02:04 AM

    I understand it is possible to call Java classes from COBOL programs using the INVOKE command. I have seen the example in the Prog.Guide.
    I need some clarification on passing parameters to Java and returning values from Java.

    What type of parameters can be passed on the INVOKE command when calling a JAVA class from COBOL ?

    Suppose I have an in-core buffer that I'd like the Java class to manipulate? can this buffer be made available to the Java class ?
    Is it possible to pass pointer variables to Java? (an address pointing to some storage structure) 
    The goal is to convert some CPU intensive subroutines that manipulate storage structures into Java classes in order to leverage the Zaap processor.
    Is there any useful documentation on this subject ?

    danik56


  • 2.  Re: Calling Java from Enterprise COBOL program

    Posted Mon June 19, 2017 06:21 PM

    You can't really pass a pointer variable from COBOL to Java.  That's because the only pointers that you can pass to Java are pointers to Java objects.  So what you need to do is manufacture a Java object that allows Java to look at the COBOL data.  Probably the easiest way to do that is with something called Direct Byte Buffer.  You can only set up a direct byte buffer referring to your COBOL buffer in JNI.  So now it depends up which version of the compiler you have.  If you have V6.1, you're in luck:  the JNINativeInterface has been updated and you can do something like:

    CALL NewDirectByteBuffer Using by value env-ptr by value pointer-variable

    Likely you have an older COBOL Compiler....  Here is an example that doesn't actually use INVOKE but can be adapted:

           Identification Division.
           program-id. "CBL2JAVA" RECURSIVE.
           Data Division.
            Working-Storage Section.
              1 MyGroup.
                2 field1 pic x(5) value 'Allan'.
                2 field2 pic x(5) value 'Henry'.
                2 field3 pic S9(9) binary value 011235813.
              1 MyGroupLen pic S9(9) binary.

           Procedure Division.
               MOVE LENGTH OF MyGroup To MyGroupLen
               Display "Before " field1 ", " field2 ", " field3;
               *> CStub is a C++ program to :
               *>   calls NewDirectByteBuffer
               *>   load Java program com/ibm/COBOL
               *>   run procedure testProc(ByteBuffer)
               Call "CStub" Using by reference MyGroup
                                  by value MyGroupLen
               Display "After " field1 ", " field2 ", " field3;
               MOVE 0 TO RETURN-CODE
               goback.
           End program "CBL2JAVA".

     

    The CStub (C++) program connects to the JVM (the same JVM used by OO COBOL) and does this:

          void getDBBAndCallMethod(jint myGroupAddr, jint myGroupLen)
             {
             jobject dbb = JNI()->NewDirectByteBuffer((void *) myGroupAddr, myGroupLen);   // Turns the COBOL group into a direct byte buffer
             JNI()->CallStaticVoidMethod(hgc_.myClass(), myMethod_, dbb);    // calls test procedure
             }

     

    And the Java program does this

    package com.ibm.COBOL;

    public final class CBL2Java
       {
       public static void testProc(java.nio.ByteBuffer cobData)
          {
          cobData.rewind();
          byte b1[] = new byte[5];
          for (int i = 0; i < b1.length; ++i)
             {
             b1[i] = cobData.get();
             }
          String s1 = new String(b1);
          byte b2[] = new byte[5];
          for (int i = 0; i < b2.length; ++i)
             {
             b2[i] = cobData.get();
             }
          String s2 = new String(b2);
          System.out.println("s1 = " + s1);
          System.out.println("s2 = " + s2);
          int l = cobData.getInt();
          System.out.println("l = " + l);
          cobData.rewind();
          byte[] mbytes;
          try
             {
             cobData.put(mbytes = (new String("ABCDE").getBytes()), 0, mbytes.length);
             }
          catch (Exception e)
             {
             System.out.println("Got exception on put1 " +e);
             }
          try
             {
             cobData.put(mbytes = (new String("FGHIJ").getBytes()), 0, mbytes.length);
             }
          catch (Exception e)
             {
             System.out.println("Got exception on put1 " +e);
             }
          try
             {
             cobData.putInt(b1.length + b2.length, 987654321);
             }
          catch (Exception e)
             {
             System.out.println("Got exception on put2 " +e);
             }
          }
       }

     

    There are probably lots of other ways to do what you want to do.  But if the goal is to pass an unmodified COBOL group to Java, probably a Direct Byte Buffer is the most efficient way.  Of course, the Java program must carefully understand the byte layout of the COBOL group.

    ahkielstra


  • 3.  Re: Calling Java from Enterprise COBOL program

    Posted Tue June 20, 2017 01:41 AM

    Thanks a lot for the detailed response.  I will experiment based on the provided input.

    I also realize that in CICS passing data from COBOL to JAVA can be done utilizing Channel & Container facilities. Is that the most efficient way ?

    danik56


  • 4.  Re: Calling Java from Enterprise COBOL program

    Posted Tue June 20, 2017 09:10 AM

    Yes, if your program is structured for CICS  style parameter passing, Channels and Containers are well optimized for efficiently passing data between COBOL and Java.

    ahkielstra


  • 5.  Re: Calling Java from Enterprise COBOL program

    Posted Tue June 20, 2017 12:41 PM

    I currently have the COBOL compiler version 4.1 and Java SDK 6.0

    I was able to use INVOKE and successfully call a JAVA method while passing a string argument as input.

    Question is can I still use INVOKE and directly call a Java method passing a byte array without involving the C stub ?

    danik56


  • 6.  Re: Calling Java from Enterprise COBOL program

    Posted Tue June 20, 2017 01:52 PM

    Basically, yes.  The code that I showed above is code to wrap a COBOL group in a java.nio.ByteBuffer and, more specifically, a direct byte buffer (dbb.)  The direct byte buffer is probably the fastest way to share COBOL data with Java.

     

    You will need SOME C or C++ code to do this.  The reason for that is:  pretty much the only way to create a direct byte buffer is via JNI.  (I think there might also be a private member in java.nio that our Just In Time (JIT) compiler uses to make direct byte buffers.)

    Somewhere early in your program you want to INVOKE a native Java method so that it can make up the direct byte buffer for you.  After that, you can use INVOKE to call other Java methods and pass in the direct byte buffer.

    The native method needs two arguments:  the address of the group and the length of the group.  The simplest way to set these up is:

             1 addressOfMyGroup usage pointer.
             1 lengthOfMyGroup pic S9(9) binary.

             1 dbbMyGroup Object-Reference.

           Procedure Division.
               set addressOfMyGroup to address of MyGroup
               MOVE LENGTH OF MyGroup To lengthOfMyGroup

     

               INVOKE myClass myNativeMethod Using BY VALUE addresOfMyGroup

                                                                                  BY VALUE lengthOfMyGroup

                                                                                  RETURNING dbbMyGroup

     

    Now you can invoke any Java method with dbbMyGroup.  On the Java side, it will be declared as a java.nio.ByteBuffer

     

    The native method should probably be a static method.  In the C++ implementation, it should probably be declared

    extern "C"  jobject Java_blah_myClass_blah_myNativeMethd(jint addressOfMyGroup, jint lengthOfMyGroup);

    This is really horrible because we are completely thwarting the type system by telling the compiler that the address is a jint.  But it will work.  (Until we invent 64-bit COBOL and you migrate to 64-bit COBOL...but that is for another day.)  The native method should do a couple of things:

    * call the JNI function NewDirectByteBuffer

    * return that result to COBOL

    * I would also store the result in a static member of myClass

    The reason I would store the reference to the direct byte buffer in a static member of myClass is an abundance of caution.  When COBOL invokes the native method, the JVM knows you are running JNI code and objects that you create are safe from garbage collection.  However, once you return to COBOL, you are now not in Java code (on your thread.)  If something else is running on the JVM (say on another thread) the JVM has no way of knowing that you're storing a reference to a direct byte buffer object in a COBOL program.  Thus, it could re-claim this object.  Storing the reference in a static member of your class (while still in the original native method) will stop any garbage collection of the dbb object.  (If you are a really thorough programmer, when you are through using the dbb in any COBOL context , you should set the static member to null.)

     

    (Obviously, you don't need to call a native method directly from COBOL.  You can call a Java method that, in turn, calls the native method to do the JNI call to NewDirectByteBuffer.  The Java method can then store the reference to a static member.)

    When you get to COBOL V6.1, you can call NewDirectByteBuffer directly from COBOL.  (Because the JNI copybook is updated.)  So, theoretically, you don't need any C.  I would still consider a Java native method in that case so that the JVM does not have a chance to garbage collect the result of the NewDirectByteBuffer call.

    ahkielstra