本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
AWS 现代化应用程序的 Blu Age 结构
本文档详细介绍了现代化应用程序的结构(使用 AWS 大型机现代化重构工具),以便开发人员可以完成各种任务,例如:
-
顺畅地导航到应用程序。
-
开发可从现代化应用程序中调用的自定义程序。
-
安全地重构现代化应用程序。
我们假设您已经掌握了以下方面的基础知识:
-
传统的常见编码概念,例如记录、数据集及其对记录(索引、顺序)、运行单元VSAM、jcl 脚本、CICS概念等的访问模式。
-
使用 Spring 框架
进行 java 编码。 -
为方便阅读,我们在整篇文档中使用了
short class names
。有关更多信息,请参阅AWS Blu Age 完全限定名称映射检索 AWS Blu Age 运行时元素的相应完全限定名称和第三方完全限定名称映射检索第三方元素的相应完全限定名称。 -
所有工件和样本均取自样本COBOL/CICSCardDemo 应用程序
的现代化过程输出。
构件组织
AWS Blu Age 现代化应用程序打包为 java Web 应用程序 (.war),您可以将其部署在服务器上。JEE通常,服务器是嵌入了 AWS Blu Age Runtime 的 Tomcat
war 聚合了几个组件构件 (.jar)。每个 jar 都是专用 java 项目的编译(使用 maven
基本组织依赖以下结构:
-
实体项目:包含业务模型和上下文元素。项目名称通常以“-entities”结尾。通常,对于给定的遗留COBOL程序,这相当于I/O部分(数据集)和数据部门的现代化。您可以有多个实体项目。
-
服务项目:包含传统业务逻辑现代化元素。通常是程序的程序划分。COBOL您可以有多个服务项目。
-
实用程序项目:包含其他项目使用的共享常用工具和实用工具。
-
Web 项目:包含 UI 相关元素的现代化(如果适用)。不用于仅限批处理的现代化项目。这些 UI 元素可能来自CICSBMS地图、IMSMFS组件和其他大型机 UI 来源。您可以有多个 Web 项目。
实体项目内容
注意
以下描述仅适用于COBOL和 PL/I 现代化输出。RPG现代化产出基于不同的布局。
在进行任何重构之前,实体项目中的包组织与现代化程序相关联。您可以通过几种不同的方法来实现这一点。首选方法是使用重构工具箱,该工具箱在您触发代码生成机制之前运行。这是一项高级操作, BluAge 培训中将对此进行了解释。有关更多信息,请参阅重构研讨会
项目相关类
每个现代化程序都与两个包相关:business.context 包和 business.model 包。
-
base package
.program
.business.contextbusiness.context 子包包含两个类,即一个配置类和一个上下文类。
-
程序的一个配置类,其中包含给定程序的特定配置详细信息,例如用于表示基于字符的数据元素的字符集、用于填充数据结构元素的默认字节值等。类名称以“Configuration”结尾。该类标有
@org.springframework.context.annotation.Configuration
注释,并且包含一个必须返回正确设置的Configuration
对象的方法。 -
一个上下文类,用作程序服务类(见下文)与模型子包(见下文)中的数据结构 (
Record
) 和数据集 (File
) 之间的桥梁。该类名称以“Context”结尾并且是RuntimeContext
类的子类。
-
-
base package
.program
.business.model模型子包包含给定程序可以使用的所有数据结构。例如,任何 01 级COBOL数据结构都对应于模型子包中的一个类(较低级别的数据结构是它们所拥有的 01 级结构的属性)。有关如何对 01 数据结构进行现代化的更多信息,请参阅Bl AWS u Age 中的数据简化器是什么。
所有类都扩展了 RecordEntity
类,该类表示对业务记录表示的访问权限。有些记录有特殊用途,因为它们绑定到 File
。a Record
和 a 之间的绑定File
是在创建文件对象时在上下文类中找到的相应的 * FileHandler 方法中进行的。例如,以下清单显示了如何绑 TransactfileFile File
定到 transactFile Record
(来自模型子包)。
服务项目内容
每个服务项目都附带一个专用的 SpringbootSpringBootLauncher
的类实现的,该类位于服务 java sources 的基础包中:
该类主要用于:
-
为程序类和托管资源(数据源/事务管理器/数据集映射/等...)之间建立联系。
-
为程序提供
ConfigurableApplicationContext
。 -
发现所有标记为 spring 组件 (
@Component
) 的类。 -
确保程序在
ProgramRegistry
中正确注册。请参阅用于进行此注册的 initialize 方法。
程序相关构件
在未进行事先重构的情况下,业务逻辑现代化输出将按每个传统程序两个或三个包进行组织:
最详尽的情况下将提供三个包:
-
base package.program.service
:包含一个名为 ProgramProcess 的接口,该接口具有处理业务逻辑的业务方法,保留了传统的执行控制流。 -
base package.program.service.impl
: 包含一个名为 Prog ra ProcessImpl m 的类,它是前面描述的 Process 接口的实现。这是依靠 AWS Blu Age 框架将遗留语句 “翻译” 为 java 语句的地方: -
base package.program.statemachine
:该包有时可能不存在。当传统控制流的现代化必须使用状态机方法(即使用 Spring StateMachine 框架)来正确覆盖遗留的执行流程时,这是必需的。 在这种情况下,状态机子包包含两个类:
-
ProgramProcedureDivisionStateMachineController
:该类扩展实现StateMachineController
(定义控制状态机执行所需的操作)和StateMachineRunner
(定义运行状态机所需的操作)接口的类,用于驱动 Spring 状态机机制;例如,示例用例中的SimpleStateMachineController
。状态机控制器定义了可能的不同状态以及状态之间的转换,可重现给定程序的传统执行控制流。
在构建状态机时,控制器会引用状态机包的关联服务类中定义的方法,如下所述:
subConfigurer.state(States._0000_MAIN, buildAction(() -> {stateProcess._0000Main(lctx, ctrl);}), null); subConfigurer.state(States.ABEND_ROUTINE, buildAction(() -> {stateProcess.abendRoutine(lctx, ctrl);}), null);
-
ProgramProcedureDivisionStateMachineService
:该服务类代表一些业务逻辑,这些逻辑需要与状态机控制器创建的状态机绑定,如前所述。该类方法中的代码使用状态机控制器中定义的事件:
状态机服务还会调用前面所述的过程服务实现:
-
除此之外,名为 base package.program
的包也发挥重要作用,因为该包为每个程序收集一个类,该类将作为程序的入口点(稍后会详细介绍)。每个类都实现 Program
接口,即程序入口点的标记。
其他构件
-
BMSMAPs同伴
除了与程序相关的构件外,服务项目还可以包含用于各种用途的其他构件。对于CICS在线应用程序的现代化,现代化过程会生成一个 json 文件并放入 /src/main/resources 文件夹的地图文件夹中:
Blu Age 运行时使用这些 json 文件将SENDMAP语句使用的记录与屏幕字段绑定。
-
Groovy 脚本
如果旧版应用程序有JCL脚本,则这些脚本已现代化为 gr oovy
脚本,存储在 /src/main/resources/scripts 文件夹中(稍后会详细介绍该特定位置): 这些脚本用于启动批处理作业(专用、非交互式、CPU 密集型数据处理工作负载)。
-
SQL文件
如果旧版应用程序使用SQL查询,则相应的现代化SQL查询已收集在专用属性文件中,命名模式程序为.sql,其中 program 是使用这些查询的程序的名称。
这些 sql 文件的内容是 (key=query) 条目的集合,其中每个查询都与一个唯一的键相关联,现代化程序使用该键来运行给定查询:
例如,COSGN00C 程序正在使用键 “COSGN00C_1”(sql 文件中的第一个条目)执行查询:
实体项目内容
名字以“-tools”结尾的实用程序项目包含一组技术实用工具,这些工具可用于所有其他项目。
Web 项目内容
Web 项目仅在对传统 UI 元素进行现代化时才存在。用于构建现代化应用程序前端的现代 UI 元素基于 Angular
Web 项目仅处理应用程序的前端方面。服务项目依赖于公用事业和实体项目,提供后端服务。前端和后端之间的链接是通过名为 Gapwalk-Application 的 Web 应用程序建立的,该应用程序是标准 AWS Blu Age 运行时发行版的一部分。
运行和调用程序
在遗留系统上,程序被编译为独立的可执行文件,这些可执行文件可以通过诸如COBOLCALL语句之类的CALL机制调用自己,在需要时传递参数。现代化应用程序提供了相同的功能,但使用了不同的方法,因为所涉及的构件的性质与传统构件的性质不同。
在现代化系统上,程序入口点是实现 Program
接口的特定类,是 Spring 组件 (@Component),位于服务项目中名为 base package.program
的包中。
程序注册
每次启动托管现代化应用程序的 TomcatProgramRegistry
的专用注册表中填充了程序条目,每个程序都使用其标识符进行注册,每个已知的程序标识符对应一个条目,这意味着如果一个程序具有多个不同的已知标识符,则注册表包含的条目与标识符的数量相等。
给定程序的注册依赖于 getProgramIdentifiers () 方法返回的标识符集合:
在此示例中,该程序注册了一次,名为 “CBACT04C”(查看 programIdentifiers 集合的内容)。Tomcat 日志显示每个程序的注册情况。程序注册仅取决于声明的程序标识符,而不取决于程序类名词本身(尽管通常程序标识符和程序类名称是一致的)。
同样的注册机制适用于各种实用程序 AWS Blu Age Web 应用程序带来的实用程序,这些应用程序是 AWS Blu Age 运行时发行版的一部分。例如,Gapwalk-Utility-Pgm 网络应用程序提供了 z/OS 系统实用程序(IDCAMS、、等)的等效功能 ICEGENERSORT,并且可以由现代化的程序或脚本调用。在 Tomcat 启动时注册的所有可用实用程序都记录在 Tomcat 日志中。
脚本和进程守护程序注册
在 Tomcat 启动时,位于 /src/main/resources/scripts 文件夹层次结构中的 groovy 脚本也会发生类似的注册过程。遍历脚本文件夹层次结构后,所有发现的 groovy 脚本(特殊的 functions.groovy 保留脚本除外)都将注册到 ScriptRegistry
中,并使用其短名称(脚本文件名中位于第一个点字符之前的部分)作为密钥进行检索。
注意
-
如果多个脚本的文件名会导致生成相同的注册密钥,则仅注册最新的脚本,并覆盖该给定密钥之前的任何注册。
-
考虑到上述注意事项,使用子文件夹时需引起注意,因为注册机制会使层次结构扁平化并可能导致意外覆盖。层次结构不计入注册过程:通常 /scripts/A/myscript.groovy 和 /scripts/B/myscript.groovy 会导致 /scripts/B/myscript.groovy 覆盖 /scripts/A/myscript.groovy。
/src/main/resources/daemons 文件夹中的 groovy 脚本的处理方式略有不同。它们仍被注册为常规脚本,但此外,它们仅在 Tomcat 启动时以异步方式启动一次。
在中注册脚本后ScriptRegistry
,REST调用即可使用 Gapwalk-Application 公开的专用端点启动脚本。有关更多信息,请参阅相应文档。
程序调用程序
每个程序都可以将另一个程序作为子程序进行调用,并向其传递参数。程序使用ExecutionController
接口的实现来实现此目的(大多数情况下,这将是一个ExecutionControllerImpl
实例),以及一种名为 the 的流畅API机制CallBuilder
来构建程序调用参数。
所有程序的方法都将 RuntimeContext
和 ExecutionController
作为方法参数,因此 ExecutionController
始终可用于调用其他程序。
例如,参见下图,它显示了 CBST 03A 程序如何将 03 CBST B 程序作为子程序调用,并向其传递参数:
-
ExecutionController.callSubProgram
的第一个参数是要调用的程序的标识符(即用于程序注册的标识符之一,请参阅以上段落)。 -
第二个参数是在
CallBuilder
上构建的结果,是一个Record
数组,对应于从调用方传递给被调用方的数据。 -
第三个也是最后一个参数是调用者
RuntimeContext
实例。
所有三个参数都是必需的,不能为空,但第二个参数可以是空数组。
被调用方仅在最初设计为能够处理传递的参数时才能处理这些参数。对于遗留COBOL程序来说,这意味着要有一个LINKAGE章节和一个USING条款供程序部门使用这些LINKAGE元素。
例如,请参阅相应的 CBSTM03B。 CBL
因此,CBSTM03B 程序采用单个Record
作为参数(大小为 1 的数组)。这就是他们使用 byReference () 和 getArguments () 方法链接构建的内容。CallBuilder
f API l CallBuilder
uent 类有几种方法可用于填充要传递给被调用者的参数数组:
-
asPointer(RecordAdaptable): 通过引用添加指针类型的参数。指针表示目标数据结构的地址。
-
byReference(RecordAdaptable): 通过引用添加参数。调用方将看到被调用方执行的修改。
-
byReference(RecordAdaptable): 先前方法的可变参数变体。
-
byValue(对象):添加一个按值转换为 a
Record
的参数。调用方将看到被调用方执行的修改。 -
byValue(RecordAdaptable): 与前面的方法相同,但参数可以直接作为
RecordAdaptable
. -
byValueWith边界(Object、int、int):添加一个参数,将其转换为 a
Record
,按值提取由给定边界定义的字节数组部分。
最后,该 getArguments 方法将收集所有添加的参数,并将它们作为数组返回Record
。
注意
调用方需确保参数数组具有所需的大小,在内存布局与 linkage 元素的预期布局方面,项目顺序正确且兼容。
调用程序的脚本
从 groovy 脚本中调用注册程序需要使用用于实现 MainProgramRunner
接口的类实例。通常,获得这样的实例是通过 Spring 的 ApplicationContext 使用来实现的:
MainProgramRunner
接口可用后,使用 runProgram 方法调用程序并将目标程序的标识符作为参数传递:
在前面的示例中,作业步骤调用IDCAMS(文件处理实用程序),提供实际数据集定义与其逻辑标识符之间的映射。
在处理数据集时,传统程序大多使用逻辑名称来标识数据集。当从脚本调用程序时,脚本必须将逻辑名称与实际的物理数据集进行映射。这些数据集可以位于文件系统上、Blusam 存储器中,甚至可以由内联流、多个数据集的串联或生成的来定义。GDG
使用该 withFileConfiguration 方法构建数据集的逻辑到物理映射,并将其提供给被调用的程序。
自行编写程序
自行编写程序供脚本或其他现代化程序调用是一项常见任务。通常,在现代化项目中,当可执行的传统程序是使用现代化过程不支持的语言编写的,或者源代码丢失(是的,可能会发生这种情况),或者该程序是一个源代码不可用的实用程序时,您需要自行编写程序。
在这种情况下,您可能必须自己使用 java 编写缺失的程序(前提是您对程序的预期行为、程序参数的内存布局(如果有)等有足够的了解。) 您的 java 程序必须符合本文档中描述的程序机制,以便其他程序和脚本可以运行它。
要确保该程序可用,您必须完成两个必需的步骤:
-
编写一个能正确实现
Program
接口的类,以便可以注册和调用该接口。 -
确保您的程序已正确注册,以便其他程序/脚本可以看到该程序。
编写程序实现
使用你的IDE来创建一个实现该Program
接口的新 Java 类:
下图显示了 EclipseIDE,它负责创建所有要实现的强制方法:
Spring 集成
首先,必须将该类声明为 Spring 组件。为该类添加 @Component
注释:
接下来,正确实现所需的方法。在本示例中,我们将 MyUtilityProgram
添加到已包含所有现代化程序的包中,该位置允许程序使用现有的 Springboot 应用程序来提供 getSpringApplication 方法实现ConfigurableApplicationContext
所需的内容:
您可以为自己的程序选择不同的位置。例如,您可以将给定的程序放在另一个专门的服务项目中。确保给定的服务项目有自己的 Springboot 应用程序,这样就可以检索 ApplicationContext (应该是ConfigurableApplicationContext
)。
为程序添加标识
要使程序能被其他程序和脚本调用,必须为其提供至少一个标识符,该标识符不得与系统中任何现有的其他已注册程序发生冲突。标识符的选择可能是因为需要涵盖现有的旧版程序替换;在这种情况下,你必须使用预期的标识符,就像在整个传统程序中CALL出现的那样。在传统系统中,大多数程序标识符的长度为 8 个字符。
在程序中创建一组不可修改的标识符是实现此目的的一种方法。以下示例显示了选择 “MYUTILPG” 作为单个标识符:
将程序与上下文关联
程序需要一个配套 RuntimeContext
实例。对于现代化程序, AWS Blu Age 会使用传统程序中的数据结构自动生成配套上下文。
如果您正在编写自己的程序,则还必须编写配套上下文。
请参阅项目相关类,即可以看到一个程序至少需要两个配套类:
-
配置类。
-
使用配置的上下文类。
如果实用程序使用任何额外的数据结构,则还应编写相应的数据结构并使其被上下文使用。
这些类应位于包层次结构中的包中,应用程序启动时会扫描该包层次结构,从而确保上下文组件和配置由 Spring 框架处理。
我们在实体项目中新创建的 base package.myutilityprogram.business.context
包中编写一个最小配置和上下文:
以下是配置内容,其使用的配置构建与附近其他(现代化)程序类似。您可能需要根据自己的特定需求对其进行自定义。
注意:
-
一般命名约定是ProgramName配置。
-
必须使用 @org.springframework.context.annotation.Configuration 和 @Lazy 注释。
-
bean 名称通常遵循ProgramNameContextConfiguration 惯例,但这不是强制性的。确保在整个项目中避免 Bean 名称冲突。
-
要实现的单个方法必须返回一个
Configuration
对象。使用 flConfigurationBuilder
uent API 来帮助你建造一个。
相关的上下文:
注意
-
上下文类应该扩展现有的
Context
接口实现(可以是RuntimeContext
或JicsRuntimeContext
,它RuntimeContext
通过JICS特定项进行了增强)。 -
一般命名约定是ProgramName上下文。
-
必须将其声明为 Prototype 组件,并使用 @Lazy 注解。
-
构造函数引用关联的配置,使用 @Qualifier 注释来定位正确的配置类。
-
如果实用程序使用一些额外的数据结构,这些数据结构应:
-
已编写并添加到
base package.business.model
包中。 -
在上下文中引用。查看其他现有的上下文类,了解如何引用数据结构类并根据需要调整上下文方法(构造函数/清理/重置)。
-
专用上下文可用后,新程序即可使用该上下文:
注意:
-
该 getContext 方法必须如图所示严格实现,使用对
ProgramContextStore
类的 getOrCreate 方法和 auto wired Spring 的委托BeanFactory
。单个程序标识符用于在ProgramContextStore
中存储程序上下文;该标识符被称为“程序主标识符”。 -
必须使用
@Import
spring 注释来引用配套配置和上下文类。
实现业务逻辑
完成程序框架后,实现新实用程序的业务逻辑。
在程序的 run
方法中执行此操作。该方法将在任何时候被其他程序或脚本调用该程序时执行。
祝您编码顺利!
处理程序注册
最后,请确保将新程序在 ProgramRegistry
中正确注册。如果您将新程序添加到已包含其他程序的包中,则无需执行任何其他操作。在应用程序启动时,新程序会被选中并与其所有邻居程序一并注册。
如果您为程序选择了其他位置,则必须确保该程序在 Tomcat 启动时能够正确注册。有关如何执行此操作的一些灵感,请查看服务项目中生成 SpringbootLauncher 类的初始化方法(请参阅服务项目内容)。
查看 Tomcat 启动日志。每个程序注册都会被记录在日志中。如果您的程序成功注册,可找到匹配的日志条目。
确定程序已正确注册后,即可开始迭代业务逻辑编码。
完全限定名称映射
本节包含用于现代化应用程序的 AWS Blu Age 和第三方完全限定的名称映射列表。
AWS Blu Age 完全限定名称映射
短名称 | 完全限定名称 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
第三方完全限定名称映射
短名称 | 完全限定名称 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|