将脚本语言与原生 COBOL 集成





4.00/5 (3投票s)
如何以及将脚本与原生 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。我最喜欢的是 JPython、JRuby 和 JavaScript。
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 的脚本语言非常容易,所以请随意使用它。现在我应该使用哪种脚本语言……?