65.9K
CodeProject 正在变化。 阅读更多。
Home

将脚本语言与原生 COBOL 集成

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (3投票s)

2010年1月19日

CC (ASA 2.5)

3分钟阅读

viewsIcon

17175

如何以及将脚本与原生 Micro Focus COBOL 紧密集成,以生成更多可定制和灵活的应用程序的好处。

脚本语言和 COBOL

由 Stephen Gennard 撰写并代表其发布。

过去几年,脚本语言与其他语言的使用有所增加,从简单的互操作性、脚本代码的重用到允许您的代码通过使用外部脚本进行定制。所有这些都是我所见过的客户使用的真实世界示例。

语言之间的互操作性对 COBOL 环境来说与对其他语言一样重要。某些平台(例如微软的 .NET 及其 CLR)通过允许所有语言共享公共基础设施(即指令集和 VM(MSIL 和 CLR)以及用于入门的基类库)使生活更加轻松。

诸如 Sun 的 VM (JVM) 之类的环境提供了两种不同的方法来实现与 Java 的互操作性,第一种是通过 JNI/JNA,第二种是生成在 VM 上原样运行的字节码。

尽管 Micro Focus COBOL 编译器不支持 JVM 字节码或 Java 源代码生成,但它确实支持通过 OO invoke 动词调用类/方法。

这种机制非常容易使用,您只需要让我们的对象 COBOL 运行时知道该类是 Java 类。这可以通过在类名前面放置 $JAVA$ 并确保 JVM 可以找到该类本身来完成,通常是通过将额外的目录或 * .jar* 文件添加到 CLASSPATH 环境变量来实现。

使用 Java 6.0 和 JSR 223,通过包 javax.script 提供了对基于 Java 的脚本语言的支持。

Java 拥有丰富的脚本语言,从 AWK 到 XSLT。我最喜欢的是 JPythonJRubyJavaScript

java.net 网站提供了脚本语言的综合列表 - https://scripting.dev.java.net/

要使用脚本包,您首先需要创建一个 ScriptEngineManager,然后使用它为您选择的脚本语言创建一个特定的 ScriptEngine 对象并使用它。

例如

  • 创建 ScriptEngineManager 对象。
  • 从管理器中检索 ScriptEngine 对象。
  • 使用 ScriptEngine 对象评估脚本。

在 COBOL 中,这非常简单

*> ooctrl(+p) required for COM and Java classes
*> ooctrl(-f) used to preserve case of method names for Java
$set ooctrl(+p) ooctrl(-f)

class-control.
   cls-Script-EngineManager is
       class "$JAVA$javax.script.ScriptEngineManager"
   cls-Script-Engine is
       class "$JAVA$javax.script.ScriptEngine"
   cls-object is
       class "$JAVA$java.lang.Object"
   cls-System is
       class "$JAVA$java.lang.System"     
   cls-PrintStream is
       class "$JAVA$java.io.PrintStream"
  .

working-storage section.
01 ws-obj-sem     object reference cls-Script-EngineManager.
01 ws-javascript  object reference cls-Script-Engine.
01 ws-obj         object reference cls-object.

01 ws-pout        object reference cls-PrintStream.
procedure division.
 invoke cls-Script-EngineManager "new" 
     returning ws-obj-sem
 end-invoke

 invoke ws-obj-sem "getEngineByName" using
     "JavaScript" returning ws-javascript 
 end-invoke

 invoke ws-javascript "eval" using
     z"print('Hello, world!')"
     returning ws-obj
 end-invoke

 if ws-obj not equal null
  invoke cls-System "getout" returning ws-pout
  invoke ws-pout "println"   using ws-obj
  invoke ws-pout "finalize"  returning ws-pout
  invoke ws-obj "finalize"   returning ws-obj
 end-if

$if NO-FINALIZE not defined
 invoke ws-obj-sem "finalize"    returning ws-obj-sem
 invoke ws-javascript "finalize" returning ws-javascript
$end
stop run.

实际执行的 JavaScript 包含在 invoke 语句中,它只是

print('Hello, world!')

要使用以上示例,我们首先需要编译代码并运行它..如下所示

C:\jscripting\HelloWorld>cobol cbljscript.cbl int(); 
Micro Focus Net Express V5
Version 6.0.00059  Copyright (C) 1984-2009 Micro Focus (IP) Limited.
URN AXCGG/AA0/00000 
* Checking complete with no errors - starting code generation
* Generating cbljscript
* Data:         848     Code:        1992     Literals:         904

C:\jscripting\HelloWorld>runm cbljscript 
Micro Focus Net Express V6.0.00059                          
RUN TIME ENVIRONMENT Copyright (C) 1984-2009 Micro Focus (IP) Limited.
URN AXCGG/AA0/00000                                                             
Hello, world!

这仅仅是个开始,与另一种语言的互操作性所需的下一部分是能够将参数传入和传出脚本。幸运的是,JSR 组的聪明家伙提供了 'put' 和 'get' 方法,允许我们简单地放置一个名称参数并获取结果更新或新参数。

因此,考虑一下我们需要为脚本设置一个名为 'message' 的参数,然后在脚本执行后读取一个名为 'replyMessage' 的参数的示例。执行此操作的 JavaScript 是

/* Do some insanity checking! */
if (typeof(message) == 'undefined')
{
    message = "ERROR - 'message' has not been setup"
}

println(message)

replyMessage = "Hello from javascript"

要设置消息参数,我们只需要这样做

*> Put a variable in engine, so the javascript
*> can use it.
invoke ws-javascript "put" using
   z"message"
   z"Hello World from COBOL!"
end-invoke

然后在脚本执行后,我们只需要使用 'get' 方法..

*> get a variable in engine
  invoke ws-javascript "get" using
     z"replyMessage"
     returning ws-message
  end-invoke

*> now display the replyMessage if it is available
  if ws-message not equal null
    invoke ws-pout "println"   using ws-message
  else
    display "Javascript did not set a replyMessage var"

下面完整的 COBOL 示例也使用一个侧面文件来存放 JavaScript,代码如下

*> ooctrl(+p) required for COM and Java classes
*> ooctrl(-f) used to preserve case of method names for Java
$set ooctrl(+p) ooctrl(-f)

class-control.
   cls-Script-EngineManager is
       class "$JAVA$javax.script.ScriptEngineManager"
   cls-Script-Engine is
       class "$JAVA$javax.script.ScriptEngine"
   cls-object is
       class "$JAVA$java.lang.Object"
   cls-System is
       class "$JAVA$java.lang.System"     
   cls-PrintStream is
       class "$JAVA$java.io.PrintStream"
   cls-FileReader is
       class "$JAVA$java.io.FileReader"
  .

working-storage section.
01 ws-file        object reference cls-FileReader.
01 ws-obj-sem     object reference cls-Script-EngineManager.
01 ws-javascript  object reference cls-Script-Engine.
01 ws-obj         object reference cls-object.
01 ws-message     object reference cls-object.

01 ws-pout        object reference cls-PrintStream.
procedure division.
*> setup ws-pout to be System.out object
 invoke cls-System "getout" returning ws-pout

*> Setup a FileReader object for the external helloworld.js file
 invoke cls-FileReader "new" using
      z"helloworld.js"
      returning ws-file
 end-invoke

*> Create a new script manager
 invoke cls-Script-EngineManager "new" 
     returning ws-obj-sem
 end-invoke

*> Find the javascript engine
 invoke ws-obj-sem "getEngineByName" using
     "JavaScript" returning ws-javascript 
 end-invoke

*> Put a variable in engine, so the javascript
*> can use it.
 invoke ws-javascript "put" using
    z"message"
    z"Hello World from COBOL!"
 end-invoke

*> do some javascript stuff!
 invoke ws-javascript "eval" using
    ws-file
    returning ws-obj-sem
 end-invoke

*> get a variable in engine
 invoke ws-javascript "get" using
    z"replyMessage"
    returning ws-message
 end-invoke

*> now display the replyMessage if it is available
 if ws-message not equal null
   invoke ws-pout "println"   using ws-message
 else
   display "Javascript did not set a replyMessage var"
 end-if


*> cleanup code, not strickly needed for the example but
*> its good practice, to do it.
$if NO-FINALIZE not defined
 if ws-message not equal null
    invoke ws-message "finalize" returning ws-message
 end-if
 if ws-pout not equal null
    invoke ws-pout "finalize"    returning ws-pout
 end-if
 invoke ws-obj-sem "finalize"    returning ws-obj-sem
 invoke ws-javascript "finalize" returning ws-javascript
$end

stop run.
C:\jscripting\HelloWorld3>cobol cbljscript.cbl int(); 
Micro Focus Net Express V5
Version 6.0.00059  Copyright (C) 1984-2009 Micro Focus (IP) Limited.
URN AXCGG/AA0/00000 
* Checking complete with no errors - starting code generation
* Generating cbljscript
* Data:         888     Code:        2528     Literals:        1296

C:\jscripting\HelloWorld3>runm cbljscript 
Micro Focus Net Express V6.0.00059                          
RUN TIME ENVIRONMENT Copyright (C) 1984-2009 Micro Focus (IP) Limited.
URN AXCGG/AA0/00000                                                             
Hello World from COBOL!
Hello from javascript

正如您从上面的代码中看到的那样,设置参数非常容易,但有时我们只想在脚本语言中执行一个函数,例如

function testMessage(msg)
{
    print("testMessage : " + msg);
}

我们创建的用于使用脚本引擎的 ScriptEngine 对象可以实现一个名为 javax.script.Invocable 的可选接口;如果我们要使用的脚本引擎确实提供了此接口,则可以使用名为 invokeFunction(..) 的方法。

为了减小 COBOL 代码的大小,我在 Java 中编写了一个简单的 utils 类作为简单的代理层,代码非常简单,但确实使 COBOL 更容易使用 invokeFunction() 方法。

import javax.script.*;

public class utils {
    public static Invocable getInvocable(ScriptEngine obj) {
        return (Invocable)obj;
    }

    public static Object invokeFunction(ScriptEngine obj, 
           String function, Object p1) 
           throws ScriptException, NoSuchMethodException {
        Invocable iObj = getInvocable(obj);
        return iObj.invokeFunction(function, p1);
    }
}

然后从 COBOL 端,我们就可以使用上面的 invokeFunction

例如

*> invoke a function with one parameter
invoke cls-utils "invokeFunction" using
 ws-javascript
 z"testMessage"
 z"Hello to function testMessage from COBOL"

这在执行时给我们提供了以下输出

C:\jscripting\InvokeFunction>runm cbljscript
Micro Focus Net Express V6.0.00059
RUN TIME ENVIRONMENT Copyright (C) 1984-2009 Micro Focus (IP) Limited.
URN AXCGG/AA0/00000

testMessage : Hello to function testMessage from COBOL

完整的示例如下

*> ooctrl(+p) required for COM and Java classes
*> ooctrl(-f) used to preserve case of method names for Java
$set ooctrl(+p) ooctrl(-f)

class-control.
   cls-Script-EngineManager is
       class "$JAVA$javax.script.ScriptEngineManager"
   cls-Script-Engine is
       class "$JAVA$javax.script.ScriptEngine"
   cls-object is
       class "$JAVA$java.lang.Object"
   cls-System is
       class "$JAVA$java.lang.System"     
   cls-PrintStream is
       class "$JAVA$java.io.PrintStream"
   cls-FileReader is
       class "$JAVA$java.io.FileReader"
   cls-Utils is
       class "$JAVA$utils"
  .

working-storage section.
01 ws-file        object reference cls-FileReader.
01 ws-obj-sem     object reference cls-Script-EngineManager.
01 ws-javascript  object reference cls-Script-Engine.
01 ws-message     object reference cls-object.

01 ws-pout        object reference cls-PrintStream.
procedure division.
*> setup ws-pout to be System.out object
 invoke cls-System "getout" returning ws-pout

*> Setup a FileReader object for the external helloworld.js file
 invoke cls-FileReader "new" using
      z"helloworld.js"
      returning ws-file
 end-invoke

*> Create a new script manager
 invoke cls-Script-EngineManager "new" 
     returning ws-obj-sem
 end-invoke

*> Find the javascript engine
 invoke ws-obj-sem "getEngineByName" using
     "JavaScript" returning ws-javascript 
 end-invoke

*> do some javascript function
 invoke ws-javascript "eval" using
    ws-file
    returning ws-obj-sem
 end-invoke

*> invoke a function with one parameter
 invoke cls-utils "invokeFunction" using
    ws-javascript
    z"testMessage"
    z"Hello to function testMessage from COBOL"
    returning ws-message
 end-invoke

*> cleanup code, not strickly needed for the example but
*> its good practice, to do it.
$if NO-FINALIZE not defined
 if ws-file not equal null
    invoke ws-file "finalize" returning ws-file
 end-if
 if ws-message not equal null
    invoke ws-message "finalize" returning ws-message
  end-if
 if ws-pout not equal null
    invoke ws-pout "finalize"    returning ws-pout
 end-if
 if ws-obj-sem not equal null
    invoke ws-obj-sem "finalize"    returning ws-obj-sem
 end-if
 if ws-javascript not equal null
    invoke ws-javascript "finalize" returning ws-javascript
 end-if
$end

stop run.

结论

从 COBOL 使用基于 Java 的脚本语言非常容易,所以请随意使用它。现在我应该使用哪种脚本语言……?

© . All rights reserved.