搜索​​​​

清除过滤器
文章
姚 鑫 · 三月 18, 2021

第十二章 使用嵌入式SQL(二)

# 第十二章 使用嵌入式SQL(二) # 嵌入式SQL代码 ## 简单的SQL语句 可以将简单的SQL语句(单个嵌入式SQL语句)用于各种操作,包括: - `INSERT`,`UPDATE`,`INSERT OR UPDATE`和`DELETE`语句。 - `DDL语句。 - `GRANT`和`REVOKE`语句。 - 只返回一行的`SELECT`语句(或者如果只对返回的第一行感兴趣)。 简单的SQL语句也被称为非基于游标的SQL语句。本章稍后将介绍基于游标的嵌入式SQL。 例如,以下语句查找ID为43的(唯一的)患者的姓名: ```sql &sql(SELECT Name INTO :name FROM Patient WHERE %ID = 43) ``` 如果对可以返回多行的查询使用简单语句,则只返回第一行: ```sql &sql(SELECT Name INTO :name FROM Patient WHERE Age = 43) ``` 根据查询的不同,不能保证哪一行将首先被返回。此外,如果一个查询包含一个`INTO`语句,并且该表不包含任何数据(`SQLCODE=100)`,执行该查询将导致未定义(空)的输出主机变量。因此,在访问输出主机变量之前,所有简单嵌入式SQL语句都应该测试`SQLCODE=0`。 ## 架构名称解析 表名,视图名或存储过程名是合格的(指定架构名称)或不合格的(不指定架构名称)。如果名称未指定架构名称,则InterSystems IRIS会按以下方式解析架构名称: - 数据定义:InterSystems IRIS使用系统范围内的默认架构来解析不合格的名称。如果默认模式不存在,则InterSystems IRIS将创建模式和相应的类包。所有数据定义语句都使用系统范围内的默认架构。数据定义语句忽略`#Import`和`#SQLCompile Path`宏预处理程序指令。 - 数据管理:InterSystems IRIS对包含嵌入式SQL语句的类或例程使用`#SQLCompile`路径和/或`#Import`宏预处理程序指令指定的架构搜索路径。 `#Import`和`#SQLCompile Path`指令是具有不同功能的可能模式名称的相互独立列表。二者之一或两者均可用于为不合格的表,视图或存储过程名称提供架构名称。如果未指定架构搜索路径,则InterSystems IRIS将使用系统范围的默认架构名称。 ## 文字值 嵌入式SQL查询可能包含文字值(字符串,数字或日期)。字符串应用单引号(`'`)引起来。 (在InterSystems SQL中,双引号指定分隔的标识符): ```java /// d ##class(PHA.TEST.SQL).EmbedSQL4() ClassMethod EmbedSQL4() { &sql(SELECT 'Employee (' || Name || ')' INTO :name FROM Sample.Employee) IF SQLCODEd ##class(PHA.TEST.SQL).EmbedSQL4() Employee (Adams,Susan E.) ``` 数值可以直接使用。在InterSystems IRIS将这些文字值与字段值进行比较之前,先对文字数字和时间戳值进行`“lightly normalized”`,如以下示例所示,其中`+0050.000`被格式化为`50`: ```java /// d ##class(PHA.TEST.SQL).EmbedSQL5() ClassMethod EmbedSQL5() { &sql(SELECT Name,Age INTO :name,:age FROM Sample.Person WHERE Age = +0050.000) IF SQLCODEd ##class(PHA.TEST.SQL).EmbedSQL5() Russell,Quentin V. age=50 ``` 可以指定算术、函数和特殊变量表达式: ```java /// d ##class(PHA.TEST.SQL).EmbedSQL6() ClassMethod EmbedSQL6() { &sql(DECLARE C3 CURSOR FOR SELECT Name, Age - 65, $HOROLOG INTO :name,:retire,:today FROM Sample.Person WHERE Age > 60 ORDER BY Age, Name) &sql(OPEN C3) QUIT:(SQLCODE'=0) &sql(FETCH C3) WHILE (SQLCODE = 0) { WRITE $ZDATE(today)," ",name," has ",retire," eligibility years",! &sql(FETCH C3) } &sql(CLOSE C3) } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL6() 03/13/2021 Moon,Rhonda T. has -4 eligibility years 03/13/2021 Olsen,Ashley G. has -4 eligibility years 03/13/2021 Quixote,Terry J. has -4 eligibility years 03/13/2021 Yoders,Liza U. has -4 eligibility years 03/13/2021 Gore,Alfred M. has -3 eligibility years 03/13/2021 Houseman,Alice R. has -2 eligibility years 03/13/2021 Nichols,Heloisa M. has -2 eligibility years 03/13/2021 Houseman,Martin D. has 0 eligibility years 03/13/2021 LaRocca,David X. has 0 eligibility years 03/13/2021 Ng,Liza Z. has 0 eligibility years 03/13/2021 Smith,Elvis Y. has 0 eligibility years ``` 在嵌入式SQL中,字符串文字中不允许使用以`##`开头的一些字符序列,而必须使`##lit`指定。这些字符序列是`##;`, `##beginlit`, `##expression(`, `##function(`, `##quote(`, `##stripq(`, and `##unique(`。例如,以下示例失败: ```java ClassMethod EmbedSQL7() { WRITE "Embedded SQL test",! &sql(SELECT 'the sequence ##unique( is restricted' INTO :x) WRITE x } ``` 以下解决方法成功: ```java /// d ##class(PHA.TEST.SQL).EmbedSQL7() ClassMethod EmbedSQL7() { WRITE "Embedded SQL test",! &sql(SELECT 'the sequence ##lit(##unique() is restricted' INTO :x) WRITE x } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL7() Embedded SQL test the sequence ##unique( is restricted ``` ## 数据格式 在嵌入式SQL中,数据值处于`“逻辑模式”`。也就是说,值采用SQL查询处理器使用的本机格式。对于未定义`LogicalToODBC`或`LogicalToDisplay`转换的字符串,整数和其他数据类型,这无效。数据格式会影响`%List`数据以及`%Date`和`%Time`数据类型。 `%List`数据类型在逻辑模式下显示为以非打印列表编码字符开头的元素值。 `WRITE`命令将这些值显示为连接的元素。例如,`Sample.Person`的`FavoriteColors`字段以`%List`数据类型存储数据,例如:`$LISTBUILD('Red','Black')`。在嵌入式SQL中,这在逻辑模式下显示为`RedBlack`,长度为12个字符。在“显示”模式下,它显示为`“Red,Black”`;在ODBC模式下,它显示为`Red,Black`。在下面的示例中显示: ```java /// d ##class(PHA.TEST.SQL).EmbedSQL8() ClassMethod EmbedSQL8() { &sql(DECLARE C4 CURSOR FOR SELECT TOP 10 FavoriteColors INTO :colors FROM Sample.Person WHERE FavoriteColors IS NOT NULL) &sql(OPEN C4) QUIT:(SQLCODE'=0) &sql(FETCH C4) WHILE (SQLCODE = 0) { WRITE $LENGTH(colors),": ",colors,! &sql(FETCH C4) } &sql(CLOSE C4) } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL8() 21: ReOrangYellow 28: ReOrangYellowGreen 35: ReOrangYellowGreenGreen 36: ReOrangYellowGreeYellow 7: White 7: Black 14: GreenWhite 8:Purple 8:Yellow 10: RedRed ``` InterSystems IRIS提供的`%Date`和`%Time`数据类型使用InterSystems IRIS内部日期表示形式(`$HOROLOG`格式)作为其逻辑格式。 `%Date`数据类型在逻辑模式下返回INTEGER数据类型值;在“显示”模式下为`VARCHAR`数据类型值,在`“ODBC”`模式下为`DATE`数据类型值。 `%TimeStamp`数据类型的逻辑,显示和`ODBC`格式使用`ODBC日期-时间`格式(`YYYY-MM-DD HH:MM:SS`)。 例如,考虑以下类定义: ```java Class MyApp.Patient Extends %Persistent { /// Patient name Property Name As %String(MAXLEN = 50); /// Date of birth Property DOB As %Date; /// Date and time of last visit Property LastVisit As %TimeStamp; } ``` 针对该表的简单嵌入式SQL查询将以逻辑模式返回值。例如,考虑以下查询: ```java &sql(SELECT Name, DOB, LastVisit INTO :name, :dob, :visit FROM Patient WHERE %ID = :id) ``` 该查询将三个属性的逻辑值返回到主机变量名称,`dob`和`visit`中: 主机变量| 值 ---|--- name| "Weiss,Blanche" dob| 44051 visit| "2001-03-15 11:11:00" 请注意,`dob`是`$HOROLOG`格式。可以使用`$ZDATETIME`函数将其转换为显示格式: ```java SET dob = 44051 WRITE $ZDT(dob,3),! ``` 与`WHERE`子句中的true相同的考虑因素。例如,要查找具有给定生日的患者,必须在WHERE子句中使用逻辑值: ```java &sql(SELECT Name INTO :name FROM Patient WHERE DOB = 43023) ``` 或者,使用主机变量: ```java SET dob = $ZDH("01/02/1999",1) &sql(SELECT Name INTO :name FROM Patient WHERE DOB = :dob) ``` 在这种情况下,我们使用`$ZDATEH`函数将显示格式日期转换为其等效的`$HOROLOG`逻辑值。 ## 权限检查 嵌入式SQL不执行SQL特权检查。可以访问所有表,视图和列,并执行任何操作,而不管特权分配如何。假定使用嵌入式SQL的应用程序将在使用嵌入式SQL语句之前检查特权。 可以在嵌入式SQL中使用InterSystems SQL `%CHECKPRIV`语句来确定当前权限。
文章
Louis Lu · 一月 15, 2021

什么是核心文件,它们什么时候有用

# 什么是核心文件? 它们什么时候有用? 本文档中的信息以 2019 年 6 月 30 日发布的 InterSystems 产品最新版本为准。 此更新涵盖了截至 2020 年 4 月 14 日发现的错误,但不包括 InterSystems 产品新版本中的更改。不过,现有产品的细节不会经常变化。本文的 PDF 版本可以从 WRC 获取 目录 核心文件基础知识 SuSE Linux Windows AIX Ubuntu Linux 测试 Docker macOS (Darwin) 健全性测试 HP–UX OpenVMS 传输 RedHat Linux Solaris 索引 核心文件基础知识 Caché、Ensemble、HealthShare 和 InterSystems IRIS 数据平台非常可靠。 我们的绝大多数客户从未经历过任何种类的故障。 但是,在极少数情况下,进程发生过故障,并因此生成了核心文件(在 Windows 和 OpenVMS 上称为进程转储文件process dump file)。 核心文件记录了进程发生故障时的进程状态,包括进程寄存器和内存(是否包括共享内存的信息取决于配置)。 核心文件实质上是发生故障的进程在试图执行错误操作时的瞬时画面。 我们可以根据该画面向回推断,以找出导致故障的最初错误。 随着我们回溯过去的时间越久远,进程的信息逐渐变得模糊。 核心越详细,我们可以回溯的时间就越远,直到画面变得过于模糊。 借助正确收集的核心文件和相关信息,我们通常可以解决问题,或者以其他方式提取故障进程的有价值信息。 对于人为生成的核心文件,通常我们只能说(在经过数小时分析后):“我看到这个进程发生了什么,有人手动生成了进程核心。” 手动生成核心文件可以包含更多正在运行进程的信息,它可用作辅助信息源,以补充无法从系统默认核心文件提供的信息细节。 InterSystems 产品可以配置为在发生任何进程故障时都记录完整核心。 这对日常操作的性能没有任何影响。 您只需要保证有足够的磁盘可用空间,以应对任何潜在的、小概率发生的故障。 InterSystems 只要能获得完整核心,在解决问题方面都有着良好的记录。 有时我们会发现由难解的硬件引发的故障,并保证这种故障不会再次发生。 InterSystems 产品还可以配置为很少记录或不记录进程故障的信息。 虽然禁用核心没有性能优势,但您可能会获得运行优势。 核心文件可能包含敏感信息。 如果您不想制定保护核心文件的策略,可以只在反复出现故障后再启用生成核心文件。 In­ter­Sys­tems 产品默认采用中间方案。 也就是生成有限大小的核心文件。 利用这些小核心文件,InterSystems 通常可以识别是否为以前解决过的问题,并解决这些问题。 当然我们无法保证使用默认的有限的核心文件可以解决所有问题。 用于确定所获得的核心文件的大小和类型的主要控制参数是 DumpStyle。 这是cache.cpf 或 iris.cpf 文件中的参数。 还有其他几个特定于操作系统的控制参数。 Dump­Style 的说明在这里:。DumpStyle 取一个介于 0 到 8 之间的整数值,适用于 Caché、Ensemble、HealthShare 或 InterSystems IRIS 数据平台实例中的每个进程,并定义当进程遇到严重错误时,应保存哪种核心(或进程转储)文件。 定义的值为: 代码 名称 平台 结果 0 NORMAL UnixOpenVMSWindows 生成完整核心(取决于其他设置)。生成 CACCVIO-pid.LOG(大小受限)。生成 pid.dmp(大小受限)。 1 FULL UnixOpenVMSWindows 生成完整核心(取决于其他设置)。生成 CACHE.DMP(可能非常大)。生成 cachefpid.dmp(可能非常大)。 2 DEBUG UnixOpenVMSWindows 在 Caché2014.1 之前,生成省略共享内存的核心,现已弃用。 最好使用操作系统特定的方法省略共享内存。未实现。为 InterSystems 预留。 3 INTERMEDIATE UnixOpenVMSWindows 未实现。未实现。2014.1 版本后有效,生成cacheipid.dmp 4 MINIMAL UnixOpenVMSWindows 未实现。未实现。2014.1 版本后有效,生成cacheipid.dmp 5 NOHANDLER UnixOpenVMSWindows 不注册为信号处理程序。 将有关核心创建的所有决定都留给操作系统。未实现。未实现。 6 NOCORE UnixOpenVMSWindows 不生成核心文件。未实现。未实现。 7 NOFORK UnixOpenVMSWindows 创建核心dump(包含共享内存),但是从原始故障进程而不是从故障进程的分支副本进行创建。未实现。未实现。 8 NOFORKNOSHARE UnixOpenVMSWindows 创建不包含共享内存的核心转储,但是从原始故障进程而不是从故障进程的分支副本进行创建。未实现。未实现。 默认 DumpStyle 为 0 = NORMAL,除了在 Windows 中从 Caché 2014.1 开始,为 3 = INTERMEDIATE。 更改 DumpStyle 值的方法有三种。 分别是: ①将以下部分放在 cache.cpf 或 iris.cpf 文件中,为此,需要使用操作系统的文本编辑器: [Debug] dumpstyle=1 等号后面的数字是新的默认 DumpStyle值。 重新启动 Caché、Ensemble、HealthShare 或 InterSystems IRIS 数据平台后,如果不使用下面的方法 ② 或 ③ ,它会将这个新的值设定为所有进程的默认值。 ②使用命令: SET old=$SYSTEM.Config.ModifyDumpStyle(1) 括号中的数字是新的 DumpStyle 值。 旧值将返回。 此命令对运行后创建的所有新进程都有效。 现有进程继续以先前的 DumpStyle 运行。 此命令从 Caché 2014.1 开始有效。 对于较早的版本,可以使用以下命令: VIEW $ZUTIL(40,2,165):-2:4:1 其中新的 DumpStyle 值是最后一位数字。 ③执行以下命令或将其放在应用程序中: VIEW $ZUTIL(40,1,48):-1:4:1 其中新的 DumpStyle 值是最后一位数字。 这只对执行命令的进程有效,并覆盖方法 ① 和 ②的设定值。 大多数操作系统都可以控制将生成的核心文件重定向到一个公用目录,并控制核心文件中包含的信息数量。 您也应该手动修改这些设置,并且应考虑相应的影响,尤其是从数据隐私的角度。 以下各节介绍了各个操作系统下的详细信息。 将核心文件的输出移动到一个公用目录对于容量规划非常有用,但也可能让希望从您的站点窃取数据的人更容易访问到核心文件。 如果核心文件不包含共享内存,很多类型的问题无法从根本进行解决。 包含共享内存的核心文件往往比不包含共享内存的核心文件大得多。 大部分差异是global和routine缓冲区的大小。 如果你正在处理的是敏感信息,不含共享内存的核心文件将只包含发生故障的进程正在处理的敏感信息。 含有共享内存的核心文件还将包含每个进程最近访问的所有全局变量。 这里用到的“最近”可能表示几分钟,或者相当长的时间。 AIX 生成完整的核心文件应使用 smit 启用: System Environments > Change / Show Characteristics of Operating System > > Enable full CORE dump true > > Use pre-430 style CORE dump false 也可以从命令行使用以下命令查看: # lsattr -E -l sys0 | egrep 'fullcore|pre430core'↩ fullcore true Enable full CORE dump True pre430core false Use pre-430 style CORE dump True 使用以下命令设置: # chdev -l sys0 -a fullcore=true -a pre430core=false -P↩ `-P` 使更改永久有效。 ◆ 默认情况下,当进程发生故障时在进程的默认目录写入核心文件。 该目录通常就是主 CACHE.DAT 或 IRIS.DAT 文件所在的目录。 这可以通过 smit 更改: Problem Determination > Change/Show/Reset Core File Copying Directory 或从命令行运行: # chcore -p on -l /cores -n on -d↩ ◆ 确保文件 /etc/security/limits 的某个部分包含以下行: default: core = -1 最后,无论通过什么方式为用户进程设置环境变量,都确保每个用户都已根据需要定义或未定义 CORE_NOSHM。 如果定义了 CORE_NOSHM=1,核心文件不包含共享内存的内容。 如果定义 CORE_NOSHM=0 或完全未定义,核心文件将包含共享内存的内容。 为所有用户设置此参数简单方法是编辑 /etc/environment 并包含以下行: CORE_NOSHM=1 要分别指定每个用户是否在核心文件中包含共享内存的内容,请根据用户和他们使用的 shell 编辑以下任一文件: CORE_NOSHM=1; export CORE_NOSHM # sh in /etc/profile or $HOME/.profile export CORE_NOSHM=1 # ksh in /etc/.kshrc or $HOME/.kshrc export CORE_NOSHM=1 # bash in /etc/bashrc or ~/.bashrc setenv CORE_NOSHM 1 # csh in ~/.cshrc Docker InterSystems IRIS 数据平台 docker 容器的核心文件创建由主机 Linux 系统控制。 您必须规划将核心文件直接发送至操作系统文件。 该文件可以保存在 docker 容器内,也可以发送至映射的主机 Linux 系统上的目录中。 将核心文件发送至映射的主机 Linux 系统上的目录的优点是,它将在容器完全失效的情况下仍然保存下来。 由于核心文件必须发送到操作系统文件,因此必须在主机平台上禁用所有高级核心捕获软件。 您将需要为主机和容器系统的 /proc/sys/kernel/core_pattern 设置适当的值。 您应该选择一个相对简单并且在主机和容器上都存在的目录(/tmp 或 /cores 显然是最佳选择)。 您可能还需要包含变量,以确保来自多个 docker 容器的核心不会相互覆盖。 因此 /cores/core.%p.%e 是一个好选择。 主机操作系统 禁用 链接 RedHat Linux 必须禁用错误自动报告工具 (ABRT)。 ☞ SuSE Linux 截至 SuSE Linux Enterprise Server 11,SuSE 没有任何高级核心捕获软件。 因此您只需按照说明设置 /proc/sys/ker­nel/core_pat­tern。 但是,到目前为止,我们还没有提供在 SuSE Linux Enterprise Server 12 中禁用高级核心捕获软件的说明,因此 SuSE 12 及更高版本目前不适合作为 docker 容器的主机。 ☞ Ubuntu Linux 必须禁用 apport。 ☞ 启动容器时,您可能希望包含将用于核心文件的目录映射到主机操作系统的选项。 因此: # docker run ⋯ -v /cores:/cores ⋯ ↩ 如果不包含 -v /cores:/cores,则 docker 容器内的进程故障所创建的任何核心文件都只会在 docker 容器运行时存活。 如果 -v 选项给出的映射不是对称的,即冒号左侧和右侧的值不一样,则可能无法捕获某些核心文件内容。 将核心文件大小设置为无限制。 由于这是运行时决定,因此将以下内容添加到 docker run 命令中: # docker run ⋯ --ulimit core=-1 ⋯ ↩ HP–UX 使用以下命令使核心文件保存在公用目录中并带有扩展名称: # coreadm -e global -g /cores/core.%p.%f↩ %p 表示将 pid 放在路径名中,%f 表示将可执行文件名(如 cache 或 iris)放在路径名中。 请参见: % man 1m coreadm↩ 了解更多选项。 ◆ 使用以下命令查看是否已在核心文件中启用共享内存: # /usr/sbin/kctune core_addshmem_read↩ # /usr/sbin/kctune core_addshmem_write↩ 更改为: # /usr/sbin/kctune core_addshmem_read=1↩ # /usr/sbin/kctune core_addshmem_write=1↩ 1 表示启用,0 表示禁用。 HP–UX 将共享内存分为两种类型。 通常,InterSystems 仅使用写共享内存,但我们建议将两种类型设置为相同值。 ◆ 在 HP–UX 上,生成的核心文件大小受 maxdsiz_64bit 内核参数限制。 确保该参数设置得足够高,以便生成完整的核心文件。 可 使用以下命令查看: # /usr/sbin/kctune maxdsiz_64bit↩ 使用以下命令设置: # /usr/sbin/kctune maxdsiz_64bit=4294967296↩ 用户可以使用 ulimit -c 命令进一步限制核心文件大小。 应该从 /etc/profile、$HOME/.profile 以及其他 shell 的类似文件中删除此设置,除非您有意限制核心文件。 RedHat Linux 如果您正在运行 Rhel 6.0 或更高版本(CentOS 同样),RedHat 已增加了错误自动报告工具 (ABRT)。 此工具安装后与 Caché、En­sem­ble、HealthShare 或 In­ter­Sys­tems IRIS 数据平台不兼容。 您需要决定您希望将 ABRT 配置为支持 Caché、Ensemble、HealthShare、InterSystems IRIS 数据平台,还是禁用 ABRT。 以下标记了 ABRT 的部分适用于使用 ABRT 的情况, 而标记了 AB/RT 的部分适用于传统的不使用 ABRT 的情况。 ◆ ABRT 要使 In­ter­Sys­tems 产品与 ABRT 兼容,请确定正在运行的 ABRT 版本: # abrt-cli --version↩ 编辑 ABRT 配置文件。 名称会因 ABRT 版本的不同而有所差异: ABRT 1.x: /etc/abrt/abrt.conf ABRT 2.x: /etc/abrt/abrt-action-save-package-data.conf 如果您使用 cinstall 命令安装了 Caché、Ensemble 或 HealthShare(最常见),或使用 irisinstall 命令安装了 InterSystems IRIS 数据平台,请找到 ProcessUnpackaged= 行,将值更改为 yes。 ProcessUnpackaged = yes 否则,如果是从 RPM 模块安装的 Caché、Ensemble、HealthShare 或 In­ter­Sys­tems IRIS 数据平台,则找到 OpenGPGCheck= 行,将值更改为 no。 OpenGPGCheck = no 不管是如何安装的 Caché、Ensemble、HealthShare 或 In­ter­Sys­tems IRIS 数据平台,都找到 BlackListedPaths= 行,并添加对 in­stal­la­tion/bin 目录中的 cstat 或 irisstat的引用。 如果 Black­List­ed­Paths= 行不存在,则在末尾添加此行以及cstat 或 iris­stat 引用。 BlackListedPaths=[retain_existing_list,]installation_directory/bin/cstat 保存所做编辑,然后重新启动 abrtd: # service abrtd restart↩ 这样配置后,ABRT 会为每次进程故障创建一个新目录(在 /var/spool/abrt 或 /var/tmp/abrt 下),并在该目录中放置核心文件以及相关信息。 当进程发生错误时,执行命令: # abrt-cli --list↩ # for ABRT 1.x # abrt-cli list↩ # for ABRT 2.x­­ 这将显示最近进程故障的列表,并为每个故障提供一个目录规范。 每个目录中都将有一个 coredump文件,以及许多其他小文件,这些文件对于确定进程故障的原因非常有用。 % tar -cvzf <var>wrcnumber</var>-core.tar.gz /var/spool/abrt/<var>directory</var>/*↩ 其中 wr­c­num­ber 是 In­ter­Sys­tems 分配用来调查案例的编号。 您可以将压缩的 wrcnumber-core.tar.gz 文件发送给我们。 ◆ AB/RT 或者,可以使用以下命令禁用 ABRT: # service abrtd stop↩ # service abrt-ccpp stop↩ # ABRT 2.x only. 要永久禁用 ABRT: # chkconfig abrtd off↩ # chkconfig abrt-ccpp off↩ # ABRT 2.x only. 最后,您需要更新 /proc/sys/kernel/core_pattern,请参见下一节。 ◆ AB/RT 您可以控制核心文件的存储位置(除非您正在使用 ABRT)。 ① : 如果正在使用 ABRT,则必须跳过此步骤。 ② : 如果已禁用 ABRT,则必须执行此步骤。 ③ : 如果从未安装 ABRT,则此步骤是可选的。 编辑文件 `/proc/sys/ker­nel/core_pat­tern` 在简单的示例中,只需使用: core 添加生成核心的程序的 pid 和名称通常很有用: core.%p.%e 还可以将核心放在公用目录中: /cores/core.%p.%e 确认所有用户对所选目录都具有写访问权限。 请参见 `man core` 了解更多选项。 您应该在目录 /etc/sysctl.d 中创建一个名称以 `.conf` 为结尾的文件,并包含以下内容,从而使此更改永久有效: kernel.core_pattern=/cores/core.%p.%e ◆ ABRT AB/RT 您应该设置 /proc/self/coredump_filter以控制转储到核心文件中的内存大小。 这可以在 /etc/profile.d/some-thing.sh 文件中设置。 命令为: # echo 0x33 >/proc/self/coredump_filter↩ 所使用的具体位映射取决于您希望收集的数据级别。 bits的含义可以在 man core 中找到,对于 InterSystems 产品有意义的示例为: Bit 说明 InterSystems 是否需要 0x01 匿名私有映射。 始终需要。 0x02 匿名共享映射。 复杂问题需要。 0x04 文件支持的私有映射。 $ZF() 相关问题可能需要。 0x08 文件支持的共享映射。 $ZF() 相关问题可能需要。 0x10 转储 ELF 标头。 始终需要。 0x20 转储私有大页。 In­ter­Sys­tems 当前未使用。 0x40 转储共享大页。 In­ter­Sys­tems 当前未使用。 0x80 转储私有 DAX 页 (Rhel 8)。 In­ter­Sys­tems 当前未使用。 0x100 转储共享 DAX 页 (Rhel 8)。 In­ter­Sys­tems 当前未使用。 作为将此命令放在 shell 特定的脚本中的替代方案,您可以在启动期间进行修改。 只有使用 grub2 引导时,这些指令才适用。 您可以通过以下命令测试: # grub2-install --version↩ grub2-install (GRUB) 2.02~beta2 编辑 /etc/de­fault/grub。 更改以 GRUB_CMDLINE_LINUX_DEFAULT= 开头的行。 如果文件中不存在该行,则在末尾添加。 它应该包含: GRUB_CMDLINE_LINUX_DEFAULT="oldcmd coredump_filter=newval " 注意:old­cmd 是 GRUB_CMD­LINE_LINUX_DE­FAULT 的旧值(如果该行先前不存在,则省略)。 new­val` 是 core­dump_fil­ter 的新值,以十六进制表示,带有前导“`0x`”。 运行: # grub2-mkconfig -o /boot/grub2/grub.cfg↩ ◆ ABRT AB/RT 您应该针对所有进程设置 ulimit -c 为无限制。 这可以在文件 /etc/security/limits.conf 中全局设置。 添加以下两行: * soft core unlimited * hard core unlimited SuSE Linux 如果运行 SuSE Linux En­ter­prise Server 12 或更高版本,SuSE 现在将所有生成的核心文件存储在 sys­temd 日志中。 存储在 systemd 日志中的核心文件是临时的。 它们在系统重启后即消失。 如果需要,必须在系统重启前从 sys­temd 日志中提取核心。 要列出 systemd 日志中当前含有的核心文件: # [ systemd- ] coredumpctl list↩ 要提取按照创建了核心的 pid 选择的核心文件: # [ systemd- ] coredumpctl -o core.morename dump pid 注意:从 SuSE 12-SP2 开始,systemd- 前缀已从命令名称中删除。 建议保留此 sys­temd 行为,不要试图修改它。 如果正在运行旧版本的 SuSE Linux Enterprise(11 或更早版本),可以通过编辑文件 /proc/sys/kernel/core_pattern来控制存放核心文件的位置。 在简单的示例中,只需使用: core 通常也需要添加生成核心文件程序的 pid 和名称,使用: core.%p.%e 还可以将核心文件放在公用目录中: /cores/core.%p.%e 确认所有用户对所选目录都具有写访问权限。 请参见 man core 了解更多选项。 可以将以下几行追加到文件 etc/sysctl.conf 中来使此更改永久有效: # Make this core pattern permanent (SuSE 12 breaks this, don't use): kernel.core_pattern=/cores/core.%p.%e ◆ 您应该设置/proc/self/coredump_filter以控制转储到核心文件的内存数量。 这可以在适当的 /etc/profile.d/something.sh文件中运行。 命令为: # echo 0x33 >/proc/self/coredump_filter↩ 所使用的具体bits映射取决于您希望收集的数据级别。 bit 的含义可以在 man core 中找到,对于 InterSystems 产品有意义的示例为: Bit 说明 InterSystems 是否需要 0x01 匿名私有映射。 始终需要。 0x02 匿名共享映射。 复杂问题需要。 0x04 文件支持的私有映射。 $ZF() 相关问题可能需要。 0x08 文件支持的共享映射。 $ZF() 相关问题可能需要。 0x10 转储 ELF 标头。 始终需要。 0x20 转储私有大页。 In­ter­Sys­tems 当前未使用。 0x40 转储共享大页。 In­ter­Sys­tems 当前未使用。 0x80 转储私有 DAX 页 (SuSE 15)。 In­ter­Sys­tems 当前未使用。 0x100 转储共享 DAX 页 (SuSE 15)。 In­ter­Sys­tems 当前未使用。 作为将此命令放在 shell 特定的脚本中的替代方案,您可以在启动期间进行修改。 要执行此操作,请使用yast2。 根据您连接的是终端界面(将使用 curses 界面)还是 GUI 界面,yast2 的用户界面会有所不同。 以下说明尽量做到与界面无关。 ① : 启动 yast2 后,从菜单中选择 Sys­tem → Boot Loader。 ② : 选择 Ker­nel Pa­ra­me­ters 选项卡。 ③ : 查找 Op­tional Ker­nel Com­mand Line Pa­ra­me­ter字段。 ④ : 如果该字段尚未包含coredump_filter=0xvalue,则使用空格分隔符将其追加到该字段中。 如果已经包含该赋值,则只需编辑 value。 ⑤ : 退出菜单系统,然后重新启动。 ◆ 您应该针对所有进程设置 ulimit -c 为无限制。这可以在文件 /etc/security/limits.conf 中进行全局设置。 添加以下两行: * soft core unlimited * hard core unlimited ◆ 注意:可能需要禁用 AppArmor,它会阻止它认为不寻常的应用程序行为,而写入核心文件可能被视为不寻常。 # rcapparmor stop↩ Ubuntu Linux Ubuntu 使用 apport 捕获所有进程故障,对于使用安装包添加的软件包,会创建 apport 报告,其中包含带有附加信息的经过编码和压缩的核心内容。 当然也可以要求 apport 处理未使用 Ubuntu 的软件包管理器(Ubuntu's package manager)安装的应用程序的代码。 不幸的是,如果这样做,Canonical 会将针对未打包代码创建的 apport 报告视为为改进 Ubuntu而收集的信息,进而进行内容检查。 由于可以从 apport 报告中提取数据,您几乎肯定不希望启用对未打包代码的 apport 处理。 您的唯一选择是禁用 ap­port。 为此,请编辑 etc/de­fault/ap­port,然后编辑 en­abled= 行: enabled=0 ◆ 创建文件 /etc/sysctl.d/30-core-pattern.conf(或该目录中的任意相似名称)。 在该文件中添加: kernel.core_pattern=/cores/core.%p.%e 确保您指定用于保存核心文件的目录可公开并写入,并且有足够的磁盘空间。 请参见 man core 了解更多选项。 ◆ 您应该设置 /proc/self/coredump_filter 以控制转储到核心文件中的内存数量。 这可以在 /etc/profile.d/something.sh 文件中进行设置。 命令为: # echo 0x33 >/proc/self/coredump_filter↩ 所使用的具体 bit 映射取决于您希望收集的数据级别。 bit 的含义可以在 man core 中找到,对于 Caché 有意义的示例为: Bit 说明 InterSystems 是否需要 0x01 匿名私有映射。 始终需要。 0x02 匿名共享映射。 复杂问题需要。 0x04 文件支持的私有映射。 $ZF() 相关问题可能需要。 0x08 文件支持的共享映射。 $ZF() 相关问题可能需要。 0x10 转储 ELF 标头。 始终需要。 0x20 转储私有大页。 In­ter­Sys­tems 当前未使用。 0x40 转储共享大页。 In­ter­Sys­tems 当前未使用。 0x80 转储私有 DAX 页 (16.04LTS)。 In­ter­Sys­tems 当前未使用。 0x100 转储共享 DAX 页 (16.04LTS)。 In­ter­Sys­tems 当前未使用。 作为将此命令放在 shell 特定的脚本中的替代方案,您可以在启动期间进行修改。 只有使用 grub2 引导时,这些指令才适用。 您可以通过以下命令测试: # grub-install --version↩ grub-install (GRUB) 2.02-2ubuntu8.12­ 编辑 /etc/de­fault/grub。 更改以 GRUB_CMDLINE_LINUX_DEFAULT= 开头的行。 如果文件中不存在该行,则在末尾添加。 它应该包含: GRUB_CMDLINE_LINUX_DEFAULT="oldcmd coredump_filter=newval " 注意:old­cmd 是 GRUB_CMD­LINE_LINUX_DE­FAULT 的旧值(如果该行先前不存在,则省略)。 new­val 是 core­dump_fil­ter 的新值,以十六进制表示,带有前导“0x”。 运行: # grub-mkconfig -o /boot/grub2/grub.cfg↩ ◆ 您应该针对所有进程设置ulimit -c 为无限制。 这可以在文件 /etc/security/limits.conf 中进行全局设置。 添加以下两行: * soft core unlimited * hard core unlimited macOS(OS X、Darwin) Mac OS X 曾重命名为 OS X,后来又重命名为 ma­cOS。 所有这些操作系统均是 Ap­ple 在 Darwin 上堆叠的专有用户界面,Darwin 是 Ap­ple 从 BSD Unix 衍生出来的操作系统,理论上已发布到公共领域。 然而,以 Ap­ple 发布 Dar­win 的方式,实际上没有人会只运行 Dar­win。 In­ter­Sys­tems 产品只需要 Dar­win,但由于 Dar­win 实际上不可用,因此所有说明均基于完整的 Apple Mac OS X、OS X 或 macOS。 ma­cOS 包括 CrashRe­porter。 该工具可以自动拦截进程故障,将故障详细信息打包为文本日志,然后将数据发送给 Apple 进行分析。 CrashReporter 将捕获第三方软件(如 Caché、Ensemble、HealthShare 和 InterSystems IRIS 数据平台)的进程故障详细信息。 理论上,Ap­ple 可将这些信息转发给 In­ter­Sys­tems。 InterSystems 没有从 Ap­ple 收到 CrashReporter 日志,我们也没有开发分析这些日志的功能。 In­ter­Sys­tems 产品严格地生成核心文件。 幸运的是,CrashReporter 独立于核心文件创建。 也就是说,处理进程故障可以通过 CrashReporter 和核心文件创建这两种方式中的任意一种或两种,或者都不使用。 CrashReporter 设置可以在“系统偏好设置”→“安全和隐私”、“隐私”选项卡中设置。 面板名称和框的选择因版本而异。 在 Mac OS X 10.4 中,该面板的名称仅为“安全性”,没有相关的复选框。 在较旧的版本中,发生任何进程故障时都会向用户显示一个对话框,询问他们是否要将数据发送到 Ap­ple 进行分析。 根据您处理的数据的敏感性,您可能想要取消选中与 CrashReprter 相关的所有选项。 ◆ 在 macOS 中,启用生成核心文件的方法在不同版本之间有很大变化。 请参见下表,并使用适合您的版本的方法。 版本 代号 InterSystems 版本 方法 公测版 Kodiak 不支持 方法 1:编辑 /hostconfig Mac OS X 10.0 Cheetah 不支持 Mac OS X 10.1 Puma 不支持 Mac OS X 10.2 Jaguar 不支持 Mac OS X 10.3 Panther Caché (PowerPC) 5.0, 5.1 Mac OS X 10.4 Tiger Caché(标记 PowerPC 或 x86)5.0PowerPC, 5.1PowerPC, 5.2*, 2007.1*, 2008.1x86, 2008.2x86, 2009.1x86 方法 2:编辑 /etc/launchd.conf Mac OS X 10.5 Leopard Caché (x86) 2008.1, 2008.2, 2009.1, 2010.1 Mac OS X 10.6 Snow Leopard Caché (x86–64) 2010.1, 2010.2, 2011.1, 2012.1, 2012.2 Mac OS X 10.7 Lion Caché (x86–64) 2011.1, 2012.1, 2012.2, 2013.1, 2014.1 OS X 10.8 Mountain Lion Caché (x86–64) 2012.2, 2013.1, 2014.1, 2015.1 OS X 10.9 Mavericks Caché (x86–64) 2013.1, 2014.1, 2015.1, 2015.2, 2016.1, 2016.2 OS X 10.10 Yosemite Caché (x86–64) 2014.1, 2015.1, 2015.2, 2016.1, 2016.2 方法 3:非自动。 OS X 10.11 El Capitan Caché (x86–64) 2016.1, 2016.2, 2017.1DEV,2017.2DEV, 2018.1DEV macOS 10.12 Sierra Caché (x86–64) 2017.1, 2017.2, 2018.1 macOS 10.13 High Sierra Caché (x86–64) 2018.1, 2019.1, 2019.2 macOS 10.14 Mojave IRIS 2019.1, 2019.2 macOS 10.15 Catalina 未发布 **方法 1:**对于 OS X 10.3 (Chee­tah) 以及先前不受支持的版本:编辑文件 /host­con­fig。 查找行 CORE­DUMPS=,然后将值更改为 -YES-。 COREDUMPS=-YES- ◆ **方法 2:**对于 OS X 10.4 (Tiger) 到 OS X 10.9 (Mav­er­icks) 版本,编辑文件 /etc/launchd.conf,然后添加以下行: limit core unlimited 重新启动。 ◆ **方法 3:**对于 OS X 10.10 (Yosemite) 及更高版本,/etc/launchd.conf 已去除。 核心文件生成现在是半禁用状态。 用户必须为每个进程启用核心: % ulimit -c unlimited↩ 在允许他们的应用程序之前,特权用户必须运行: # launchctl limit core unlimited↩­­­ 然后注销,并在启动 Caché 前再次登录。 Apple 特地没有提供一个好方法来自动执行这些操作,因为他们认为默认生成核心文件是一个潜在的安全漏洞。 Apple 提供了完全禁用核心文件生成的方法。 这通过编辑文件 /etc/sysctl.conf 并添加以下行来完成: kern.coredump=0 可以通过删除该行或将值更改为 `1` 来重新启用此功能。 OpenVMS 默认情况下,Caché 和 En­sem­ble 只针对故障进程生成 CACCVIO-pid.LOG 文件。 使用这些文件只能解决相对简单的问题。 这些 CACCVIO-pid.LOG 文件将始终放在进程默认目录中(通常是 CACHE.DAT 文件的目录),只有通过更改进程默认目录才能重定向。 Caché 和 En­sem­ble 也可能生成 CERRSAVE-pid.LOG文件。 这些文件与 CACCVIO-pid.LOG 文件类似。 通常,您无需关心它们的差异。 在某些情况下,Caché 和 En­sem­ble 将同时生成两种文件以响应故障。 在迄今为止的所有情况下,CACCVIO-pid.LOG 文件首先生成,其中包含错误的完整上下文,而 CERRSAVE-pid.LOG` 文件则在进程的最后断开过程中生成,包含的有价值的信息相对较少。 如果启用了扩展进程 dumps(完整转储),它们也会放置在进程默认目录中。 但是,可以将它们重定向,方法是定义逻辑名称 SYS$PROCDMP,将其指向要存储进程dump的目录。 该逻辑名称可以在 /SYS­TEM 级别定义。 文件名将是 CACHE.DMP 或 CSESSION.DMP。 Open­VMS 还提供了逻辑名称 SYS$PROTECTED_PROCDMP。 您也应该使用 /EX­EC­U­TIVE_MODE 和 /SYS­TEM 定义该逻辑名称。 这适用于特权映像privileged images的进程故障,Caché 的某些部分也拥有特权。 OpenVMS 文档中建议将这两个逻辑名称定义到不同的目录,并为与 SYS$PRO­TECTED_PROCDMP 对应的目录设置更高的安全性。 这基于以下假设:特权映像处理的数据比非特权映像处理的数据更敏感。 如果两种数据都很敏感,将两个逻辑名称指向同一目录也是可以的。 ◆ 影响 CACCVIO-pid.LOG 和 CERRSAVE-pid.LOG 文件创建以及完整进程dump的缺陷是有历史记录的。 以下是最重要的变更。 变更 第一个版本 说明 JLC1809 Caché 2015.2 在此变更之前,大多数 CERRSAVE-pid.LOG 文件是无用的。 JO2422 Cache 2012.1 在此变更之前,生成 CERRSAVE-pid.LOG 文件的条件在创建信息有限的文件时总是会忽略 DumpStyle。 JLC1326 Caché 2011.1 在此变更之前,Itanium 平台上的 CACCVIO-pid.LOG 和 CERRSAVE-pid.LOG 文件不包含寄存器。 这严重阻碍了我们使用这些文件解决问题的能力,而只能处理简单问题。 我们仍然可以与已经解决的问题相匹配。 JLC931 和 JLC959 Cache 2007.2 在此变更之前,Itanium 平台上的 CACCVIO-pid.LOG 和 CERRSAVE-pid.LOG 文件不记录任何有用信息。 JO1968 Caché 5.2 在此变更之前,生成 CACCVIO-pid 文件的条件在创建信息有限的文件时总是会忽略 DumpStyle。 ­­ Solaris 可以使用以下命令使核心文件放在公用目录中并带有扩展名称: # coreadm -e global -g /cores/core.%p.%f -G all↩ ① %p 表示将 pid 放在路径名中。 ② %f 表示将可执行文件名(如 cache)放在路径名中。 ③ -G all 表示包括所有类型的内存,即完整核心文件内容。 省略此选项将生成默认核心文件内容,其中仍包括大部分共享内存。 核心文件中可以存储以下内容: 代码 InterSystems 使用 默认包括 stack 需要 是 heap 需要 是 shm 不使用 是 ism 不使用 是 dism Caché 共享内存 是 text 用于 $ZF() 相关故障 是 data 需要 是 rodata 不使用 是 anon 需要 是 shanon 通常较小 是 ctf 需要 是 symntab 用于 $ZF() 相关故障 否 shfile 不使用 否 all 表示包括所有类型的内存,默认不包括最后两种。 如果您需要大幅减小的核心文件大小(以节省空间,但代价是可解决的问题变少),移除 dism 共享内存可节省出大部分空间。 使用以下命令执行此操作: # coareadm -e Global -g /cores/core.%p.%f -G (default-dism)↩ 请参见: % main 1m coreadm↩ ◆ 默认情况下,用户已经 % ulimit -c unlimited↩ 您可以使用 ulimit(或在 csh 中使用 limit 命令)禁用核心,但 core­adm 通常更灵活。 因此您应该确保 ulimit 命令不会出现在 /etc/profile 或 $HOME/.profile 中,或者其他 shell 的相应文件中。 Windows Windows 下的转储文件中包含的信息完全由 cache.cpf 文件中的 DumpStyle 参数(或上文定义的其他用于更改 DumpStyle 的接口)控制。 测试 除了特定问题外,本地安全组也可能阻止核心文件的写入。 在真实条件下测试是否可以成功创建核心文件非常有用。 为此,请输入命令: USER>DO $ZUTIL(150,"DebugException")↩ 可以肯定的是,您应该在 JOB 内以交互方式测试该语句(假定您的应用程序使用 JOB 命令),甚至在应用程序内隐藏一个不会被用户意外选择到的选项。 验证是否会获得核心文件,并按照下一节的健全性检查来验证它是否是好的核心文件。 健全性测试 核心文件(和进程转储)可能非常大,并且可能包含敏感信息。 在将核心文件传输到 InterSystems 进行分析之前,最好在生成核心文件的系统或非常相似的系统上对核心文件进行健全性测试。 根据您的操作系统,请执行以下健全性测试: 操作系统 健全性测试 AIX # dbx cache core(dbx) set $stack_details(dbx) where(dbx) quit 向 WRC 开启问题时,将上述命令的输出结果也同时发送给我们。 如果您的系统未安装 dbx,则开启一个新问题。 HP–UX # gdb cache core(gdb) frame 0(gdb) while 1 > info frame > up > end(gdb) quit # adb coreadb> $cadb> $q 根据您拥有的调试器,将上述两个命令集的其中之一的输出结果发送给我们。 如果两个调试器都有,则首选 gdb(实际是 Wildebeest)。 RedHat Linux 对于所有版本的 Linux 都使用此通用健全性测试。 # gdb cache core(gdb) frame 0(gdb) while 1 > info frame > up > end(gdb) quit SuSE Linux Ubuntu Linux 向 WRC 开启问题时,将上述命令的输出结果发送给我们。 如果您的系统未安装 gdb,则开启一个新问题。 macOS (Darwin) # lldb(lldb) target create -c core(lldb) thread backtrace all(lldb) quit # gdb cache core(gdb) frame 0(gdb) while 1 > info frame > up > end(gdb) quit 将 lldb 的输出结果(如果是 OS X 10.8 (Mountain Lion) 或更高版本)或 gdb 的输出结果(如果是 Mac OS X 10.7 (Lion) 或更早版本)发送给我们。 OpenVMS $ ANALYZE/CRASH dumpfile.DMPSDA> SHOW CALL_FRAME/ALL如果仍在运行 OpenVMS v7.x 或更早版本,先前的命令不适用,请改用:SDA> SHOW CALL_FRAMESDA> SHOW CALL_FRAME/NEXT重复先前的命令,直到获得错误。SDA> QUIT $ ANALYZE/PROCESS dumpfile.DMPDBG> SHOW CALL/IMAGEDBG> QUIT 将 SDA 或debugger调试器的输出结果发送给我们,但首选 SDA 的输出。 如果您仅有 CACCVIO-pid.LOG 文件,请检查其是否为空或几乎为空。 Solaris # mdb cache core> ::stackregs> ::quit # dbx cache core(dbx) where(dbx) quit 在 Solaris 上,InterSystems 对于几乎所有应用程序都首选 dbx 调试器,但对于健全性测试,mdb 更好。 当您向 WRC 开启问题报告时,请将 mdb 或 dbx(首选 mdb)生成的堆栈跟踪信息发送给我们。 Windows 对于 Windows 进程转储,当前没有推荐的健全性检查。 请将健全性测试的详细信息附加到 WRC 案例,或通过电子邮件发送到support@intersystems.com 。 传输 请准备好向我们发送完整核心文件以及您的特定操作系统可能需要的支持文件。 我们需要知道生成了核心文件的 Caché、Ensemble、HealthShare 或 InterSystems IRIS 数据平台的准确版本。 如果您重定向了软件并包含自定义的 `$ZF()` 函数,请发送可执行文件。 (实际上,如果您总是发送可执行文件,会更加方便。) 在大多数 Unix 系统上,最好还发送可执行文件使用的库。 我们需要库的可能性因给定的平台而异。 请查阅下表: 操作系统 硬件 需要库 支持级别 AIX PowerPC 不太可能 A HP–UX PA–RISC 非常可能 C HP–UX Itanium 有可能 A Linux(所有版本) x86 有可能 A Linux(所有版本) x86_64 有可能 A Linux(所有版本) Itanium 有可能 D macOS PowerPC 不太可能 D macOS x86 不太可能 C macOS x86_64 不太可能 A OpenVMS VAX 不适用 D OpenVMS ALPHA αxp 不适用 B OpenVMS Itanium 不适用 B Solaris x86_64 非常可能 B Solaris Sparc 不太可能 B Tru64 UNIX ALPHA αxp 非常可能 D Windows x86 不适用 A Windows x86_64 不适用 A Windows Itanium 不适用 D 支持级别说明 A: 截至本文档发布之时,InterSystems 拥有用于诊断该平台上的核心文件的资源。B: 对该平台的完全支持在最近失效。 不过,InterSystems 仍然有资源来诊断平台上的核心文件。 某些诊断出的问题可能无法使用专门版本更正。C: 旧版支持。 InterSystems 可能仍然拥有有限的资源来诊断该平台上的核心文件,但是,可能无法再提供专门版本来修复所发现的任何缺陷。D: 远古支持。 In­ter­Sys­tems 未保留任何可诊断这些平台上的问题的资源。 不过一些有限的功能仍保留下来。 这些平台上的核心可能会被分析。 但不可能修复所发现的任何缺陷。 发出 ldd 命令可列出所需的库: # ldd install_directory/bin/image linux-vdso.so.1 => (0x00007fffd1320000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f23e5002000) librt.so.1 => /lib64/librt.so.1 (0x00007f23e4dfa000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f23e4af0000) libm.so.6 => /lib64/libm.so.6 (0x00007f23e47ee000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f23e45d8000) libc.so.6 => /lib64/libc.so.6 (0x00007f23e4216000) /lib64/ld-linux-x86-64.so.2 (0x00007f23e521a000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f23e3ffa000) 上面的内容包含 Rhel 7 的示例输出。 所有 Unix 系统的输出都是类似的。 install_directory`指 Caché、Ensemble、HealthShare 或 InterSystems IRIS 数据平台的安装目录。 image 对于 2018 年以前的所有产品均为 cache,对于自 2019 年以后的产品为 irisdb。如果您要发送多个文件,最好将它们放在一个压缩的容器文件中。 一般来说,.ZIP是最好的容器。 .tar.gz 也是合理的。 对于 Open­VMS,使用以下命令创建备份文件: $ BACKUP *.* [-]<var>saveset</var>.BCK/SAVE/DATA=COMPRESS 包含一个说明所发送文件的manifest会有帮助。 请准备纯文本文件形式的manifest。如果以电子方式发送数据,请不要加密文件,而是使用加密的传输方式。 您可以使用以下任意方法向 InterSystems 发送核心文件: 方法 安全性 最大大小 直接上传至 WRC 应用。上传文件前,必须开启一个 WRC 问题,上传文件后,可以选择将问题标记为更高安全性。 问题标记为更高安全性后,所有对与调查相关的文件的访问都仅限于实际进行调查的人员。 除了 300 M 的附件大小限制外,还有 60 秒的时间限制,因此如果您的有效带宽小于 42 Mbps,最大上传量会减少 安全或更高 300 M 和 60 秒 电子邮件。一般来说,除了不需要任何客户数据就可以进行调查的简单问题,应避免使用电子邮件。 示例:您刚刚在一台新计算机上安装了 Caché,但它在启动时出现故障,并生成一个小核心文件。 通过电子邮件发送该文件是合理的。 不安全 40 M 我们的 kite-works 服务器。对于任何给定问题,您都必须申请数据上传链接。 这些链接在 30 天或更短时间内过期。 这是上传安全数据的首选方法。 绝对大小限制是我们服务器上的可用空间。 但是,由于大多数客户都使用这种方法,如果您要上传大于 4G 的文件,请事先告知。 安全 > 4 G 我们的 sftp 服务器。您必须申请特定于用于问题的目录。 将为问题创建一个目录。 对于更高安全性的问题,我们创建了一个限制访问的机器(或虚拟机),并实现了一个自动流程将上传的任何文件都移动到该机器。 绝对大小限制是指我们服务器上的可用空间,如果您要上传大于 100 G 的文件,请事先告知。 更高 > 100 G 您的 ftp/sftp 服务器。您要求我们从中下载数据的服务器必须归您所有,并且您有完全控制权。 In­ter­Sys­tems 不会从任何第三方服务器下载数据。 第三方服务器被视为安全风险。 取决于您 ? Se­curLink。InterSystems 可以通过我们的 SecurLink 远程控制工具直接从您的网络上的任何已核准的计算机中下载文件。 没有绝对大小限制。 但是,如果您通过 V.90 调制解调器连接到互联网,我们下载一个 3 Gio 的核心文件需要一周的时间。 安全且更高 ? 物理介质。您可以将物理介质邮寄到您当地的 In­ter­Sys­tems 办公室。 InterSystems 可以读取介质,并将数据发送到我们的剑桥办公室,在那里进行大多数核心文件分析。 大多数办公室都可以处理 U 盘以及 ISO 9660 光盘介质。 我们的剑桥办公室可以处理多种磁带格式。 在发送任何介质之前,您应该先与 InterSystems 确认。如果通过挂号(非认证)邮件发送介质,那么数据可以被认为是安全的(可能安全性更高)。 视情况 ? 务必要记住,我们需要的文件有些是二进制文件,有些是文本文件。 对于某些文件传输方法(尤其是在不同的操作系统之间),必须指定文件是二进制还是文本,以防止文件被损坏。 索引 a ABRT ☞, ☞AppArmor ☞abrtd ☞abrt-cli ☞apport ☞ b BlackListedPaths ☞ c CACCVIO-pid.LOG ☞, ☞CACHE.DMP☞, ☞CERRSAVE-pid.LOG ☞CentOS ☞CORE_NOSHM ☞CrashReporter ☞CSESSION.DMP ☞cachefpid.dmp ☞pid.dmp ☞cachempid.dmp ☞cache.cpf ☞, ☞chcore ☞chdev ☞chkconfig ☞coreadm ☞, ☞coredumpctl ☞core_addshmem_read ☞core_addshmem_write ☞cstat ☞ d DumpStyle ☞, ☞default ☞ e /etc/environment ☞/etc/profile.d/something.sh ☞, ☞, ☞/etc/security/limits ☞/etc/security/limits.conf ☞, ☞, ☞/etc/sysctl.d ☞ f FULL ☞ g GRUB_CMDLINE_LINUX_DEFAULT ☞, ☞grub2 ☞, ☞grub2-mkconfig ☞ i INTERMEDIATE ☞irisstat ☞iris.cpf ☞, ☞ l lsattr ☞ m MINIMAL ☞maxdsiz_64bit ☞ n NOCORE ☞NOFORK ☞NOFORKNOSHARE ☞NOHANDLER ☞NORMAL ☞ o OpenGPGCheck ☞ p ProcessUnpackaged ☞pid.dmp ☞/proc/self/coredump_filter☞, ☞, ☞/proc/sys/kernel/core_pattern ☞, ☞, ☞ r RPM ☞rcapparmor ☞ s SYS$PROCDMP ☞SYS$PROTECTED_PROCDMP ☞$SYSTEM.Config.ModifyDumpStyle ☞敏感信息 ☞smit ☞systemd ☞ u ulimit -c ☞, ☞, ☞, ☞/usr/sbin/kctune ☞ y yast2 ☞ z $ZUTIL(40,1,48) ☞$ZUTIL(40,2,165)☞$ZUTIL(150,"DebugException") ☞ get
公告
Claire Zheng · 三月 1, 2022

InterSystems开发者Python竞赛优胜者决出暨热烈祝贺Wu Fatian 同学代表中国获得社区并列第三名好成绩!

Hi开发者们, 是时候公布 InterSystems开发者Python竞赛的优胜者了!准备好了吗! 这些开发者和他们的应用程序赢得了雷鸣般的掌声: 🏆 专家提名奖(Experts Nomination)- 获奖者由我们特别挑选的专家团选出: 🥇 第一名,奖金 $4,000 获奖项目 django-iris ,开发者 @Dmitry.Maslennikov 🥈 第二名,奖金 $2,000 获奖项目 appmsw-sql2xlsx ,开发者 @MikhailenkoSergey 🥉 第三名,奖金 $1,000 获奖项目 iris-python-apps ,开发者 @Muhammad.Waseem 以下为更多优胜者: 🏅 奖金$100 ,获奖项目 iris-python-dashboards ,开发者@Evgeniy.Potapov 🏅 奖金$100 ,获奖项目 GlobalToJSON-ePython-pure ,开发者 @Robert.Cemper1003 🏅 奖金$100 ,获奖项目 blockchain - [ IRIS python ] ,开发者 @davimassaru.teixeiramuta 🏅 奖金$100 ,获奖项目 AI Image Object Detector ,开发者@Yuri.Gomes 🏅 奖金$100 ,获奖项目GlobalToJSON-embeddedPython ,开发者@Robert.Cemper1003 🏅 奖金$100 ,获奖项目 IRIS-Database-and-Machine-Learning-Based-Approaches-for-Prediction-of-Spontaneous-Intracerebral-Hemo ,开发者 @Fatian.Wu 🏅 奖金$100 ,获奖项目 DIMSE Iris Dicom Fhir Service ,开发者@Ron.Sweeney1582 🏅 奖金$100 ,获奖项目 Django-Interop ,开发者 @Fernando.VianaMaia 🏅 奖金$100 ,获奖项目 IRIS Text2Audio ,开发者 @Yuri.Gomes 🏆 社区提名奖(Community Nomination)- 获得总投票数最多的应用: 🥇 第一名,奖金 $1,000 ,获奖项目 django-iris ,开发者 @Dmitry.Maslennikov 🥈 第二名,奖金 $625 ,获奖项目 appmsw-sql2xlsx ,开发者 @MikhailenkoSergey 🥉 第三名,奖金 $625 ,获奖项目 IRIS-Database-and-Machine-Learning-Based-Approaches-for-Prediction-of-Spontaneous-Intracerebral-Hemo ,开发者 @Fatian.Wu 恭喜所有优胜者! 感谢大家对本次比赛的关注和付出!
文章
Qiao Peng · 一月 10, 2021

2020线上峰会——第1天数据平台专题会议的精彩回顾

InterSystems 2020线上峰会第二周拉开帷幕。首日专题会议涵盖了大量精彩内容,包括38场主题各异的专题会议。我认为有必要回顾一些重大的公告和谈话要点。 IntegratedML 在正式发布之后公布后global availability for IntegratedML, 本周的多个专题会议都将重点介绍IntegratedML。今天,Thomas Dyar与Baystate Health人口健康项目的业务分析师Joe Cofone进行了交流,讨论InterSystems和Baystate如何利用丰富的Health Insight数据和由IntegratedML构建的预测模型来建立“机器学习沙箱”进而构建应用程序。 如果你错过了本次内容,请查看 "AD000 针对医疗应用的Health Insight& 和IntegratedML展示 ". 明天还将有3场关于IntegratedML的单独会议: "CL006 IntegratedML入门", "DA006 如何使用H2O和IntegratedML", 和 "DA005 使用IntegratedML和DataRobot进行机器学习"! 自适应分析 上周我们还公布了InterSystems IRIS自适应分析, 通过对InterSystems IRIS和IRIS医疗版进行扩展,可为分析最终用户提供更大的灵活性、可扩展性以及更高的效率。 今天有两场会议提供了更多关于这方面的信息。第一场会议简单介绍了计划的方案及其潜在收益,第二场会议对该方案能够解决的问题类型进行了演示和讨论。 我们正在寻找早期采用者,如果您有兴趣,请联系Carmen Logue。明天的会议将涉及更多有关数据仓库 和 商业智能 的信息,敬请关注。 嵌入式Python 上周主题报告中的另一项重要信息是我们在将来会提供嵌入式的Python支持,我们已经对其进行了简要介绍。借助嵌入式Python,IRIS内核可以并排运行Python和ObjectScript,以高速访问Python中的数据,并在Python和ObjectScript之间进行了桥接。其实,我们已经获得了在Objectscript中使用嵌入式Python的经验,包括加载Python库、调用方法和函数等等。我们即将举办一场关于如何在SQL中使用嵌入式Python的分享,欢迎大家观看。如果您有兴趣帮助我们开发嵌入式Python,请通过python-interest@intersystems.com.与我们联系。 Kafka 今天我们还宣布了InterSystems将为具有IRIS互操作性的Apache Kafka提供内置支持。Kafka是世界上扩展性最强(最受欢迎)的流数据服务。企业可以将其应用在消息队列、更改数据捕获等所有事件中。该集成系统支持多种系统架构与IRIS进行互操作,无需使用额外代码实现。 明天的专题会议还将继续介绍Kafka集成相关的话题,因为最新版本的InterSystems API Manager(IAM)同样添加了Kafka插件,可将API流量发布到Kafka主题。我们已经在IAM中很好地演示了开发者门户的增强功能,可以帮助您更加轻松地构建和定制真正适合公司品牌的开发者门户。我们明天将详细介绍IAM。 VS Code - ObjectScript 哇,我们已经介绍了这么多公告了,但我们想说的远不止这些。在上周的主题峰会上,我们正式发布了1.0版本的 Intersystems ObjectScript for Visual Studio Code 插件,将ObjectScript开发引入了市场上最受欢迎的IDE(集成开发环境)平台。该版本可以在所有的操作系统上运行,并支持GitHub和Azure DevOps等客户端源代码控制系统。尤其值得注意的是,这是一项源自开发者论坛的开源项目,InterSystems将和我们的开发者们将持续合作开发该项目。 DEE004 会议对该项目进行了快速概述和简短演示。可以点击此处的演示区域 或在 YouTube上查看更多的演示内容。 In DEE007 在DEE007 会议中,我们的几位产品经理和VS Code – ObjectScript项目管理委员会的两名社区成员一起为大家带来了精彩的现场问答。本周将有更多关于VS Code的内容——请查看 "DEE006 用于ObjectScript的Visual Studio代码:选择IDE/源代码控件组合" 和 "DEE005 用于ObjectScript的Visual Studio代码:服务器端源代码控件". 今天开始的 实战实验室也不可错过。 我们还提供了IntegratedML实战实验室和其他热门话题,例如下面这个话题: FHIR和API管理 FHIR一直是业界的热门话题,也是我们的合作伙伴比较感兴趣的话题,所以我们在许多专题会议中都涵盖了FHIR相关的主题。我们简要介绍了FHIR——FHIR是医疗保健可操作性的新标准,受到了美国政府的大力支持。美国政府通过ONC最终规则,对FHIR进行标准化并禁止信息封锁。英国等其他国家的政府也在授权FHIR参与所有新的数字健康计划。 IAM可妥善处理众多开发事务,例如安全性、日志记录、监视和控制对FHIR资源的访问等,从而简化FHIR应用程序的构建流程。用户可以通过开发门户轻松学习和测试FHIR查询。建议大家观看。 可以通过FHIR搜索查询患者、观察、诊断报告等FHIR资源。语法基于REST,但是必须为每个FHIR资源查找查询参数。使用修饰符和连接(例如 _include和_revinclude)的高级查询有时会出现令人困惑的语法,不过我们构建了一个工具来简化这些查询,从而提高生产效率并减轻初学者的负担。 NLP 在线上峰会的推动下,我们还发布了开源NLP(自然语言处理)库iKnow的1.0版本,现已完全以Python包的形式提供。Aohan和Benjamin在DA010专题会议中对这一激动人心的版本进行了概述及使用介绍,相关演示可在 演示区.查看。 演示区 您可以在线上峰会注册中心主区打开The 演示区 ,并按照自己的节奏观看超过25个的演示。 现场问答 第一天结束时我们进行了现场问答,明后两天也会有现场问答环节。大家可以借此机会,针对专题会议中或上周主题演讲中遇到到的任何问题向我们提问。更多信息请参见: https://community.intersystems.com/post/ask-intersystems-product-managers-live-qa-session-vsummit20 这绝对是忙碌的一天!接下来还有两天的专题会议。如果您还没有注册,现在还为时不晚,您可以点击这里 免费注册。 那么,您今天关注的重点是什么呢?请在下方的评论中告诉我们。 祝好! Jeff
文章
姚 鑫 · 九月 16, 2022

第三十四章 在 Windows 上使用 IRIS(一)

# 第三十四章 在 Windows 上使用 IRIS(一) 在 `Microsoft Windows` 平台上管理 `IRIS` 数据平台实例非常简单。可以使用管理门户和 `IRIS` 启动器执行大多数任务。还可以从命令提示符控制 `IRIS` 实例。 本主题使用 `install-dir` 来指代 `IRIS` 安装目录——可以在安装指南的默认安装目录部分找到默认目录。 **注意:不要对 `IRIS IRIS.DAT` 数据库文件使用 `Windows` 文件压缩。 (通过右键单击 `Windows` 资源管理器中的文件或文件夹并选择属性,然后选择高级,然后压缩内容以节省磁盘空间来压缩文件;压缩后,文件夹名称或文件名在 `Windows` 资源管理器中呈现为蓝色。)如果压缩一个`IRIS.DAT` 文件,它所属的实例将无法启动,并出现误导性错误。** # 管理对 `IRIS` 实例的访问 ## `InterSystems` 服务 所有 `IRIS job`和进程都从 `InterSystems` 服务、 `IRIS Controller for ` 运行。 `InterSystems` 服务具有的权限由其关联的 `Windows` 用户帐户决定。当这是本地 `SYSTEM` 帐户时, `IRIS` 可以访问 `Windows` 系统上的所有文件和权限。为了维护一个更安全和限制性更强的环境,应该为服务选择一个仅具有所需权限和访问权限的 `Windows `帐户。 在正常安装和锁定安装中, `IRIS` 创建两个本地用户组来授予对实例的访问权限。当为 `InterSystems` 服务指定一个 `Windows` 用户帐户而不是默认的本地 `SYSTEM` 帐户时 `IRIS` 将该 `Windows` 用户帐户添加到每个组。这些组是: - `IRISServices`,它授予启动、停止和控制 `IRIS` 实例的权限。 - `IRIS_Instance_instancename`,授予对安装树的访问权限——`IRIS` 的安装目录及其所有子目录。 **注意:这些组可能不会授予 `IRIS` 执行某些操作所需的所有权限。要确保 `IRIS` 对安装树之外的所有实例、日志和日志文件具有所需的访问权限,请授予 `IRIS_Instance_instancename` 组对这些文件和包含它们的目录的完全访问权限。如有必要,还可以授予该组额外的权限。** 通常,在安装期间为 <instance-name> 选择 `IRIS` 控制器的 `Windows` 帐户,如安装指南的 `Windows` 用户帐户部分所述。 ## 限制对安装树的访问 默认情况下,任何经过身份验证的 `Windows` 用户都可以访问安装树,这可能是不可取的。以下命令为经过身份验证的用户删除 `Windows` 访问控制条目 (ACE): ```java icacls /remove "NT AUTHORITY\Authenticated Users" ``` 运行此命令后,只有管理员用户或 `IRIS_Instance_instancename` 组中的用户才能访问安装树。 **重要提示:如果不这样做,任何可以登录到主机 `Windows` 系统的用户都可以轻松地修改文件、更改设置或完全禁用 `IRIS` 实例。** 在某些情况下,除了 `IRIS` 控制器用于 ` ` 服务的帐户之外,可能还希望授予另一个 `Windows` 帐户访问安装树的权限。例如,这可能包括运行自动化任务的帐户,或直接登录到 `Windows` 服务器以访问 `IRIS` 的帐户(通过本地终端会话或调用自定义调用可执行文件)。可以通过将任何此类帐户添加到 `IRIS_Instance_instancename` 组来为其提供所需的访问权限。 # 更改 InterSystems 服务帐户 在命令行中输入以下内容以更改用于 `IRIS Controller for `的` Windows` 用户帐户: ```java \bin\IRISinstall.exe setserviceusername ``` 此命令将 `Windows` 用户帐户更改为指定的帐户。它还将用户添加到 `IRISServices` 和 `IRIS_Instance_instancename` 组,并在必要时创建这些组。运行此命令并重新启动 `IRIS` 实例后,该实例将在新指定的 `Windows` 用户帐户下运行。
问题
li wang · 四月 23, 2022

浏览器地址中输入HealtConnect发布的webService地址报CSP应用程序错误

各位老师,你们好 我在浏览器地址中输入HealtConnect发布的webService地址报 CSP应用程序错误,并记录了此错误,不过该地址还是能正常访问。 我想知道怎么解决这种报错问题,谢谢! 以下图片为HealthConnect的版本,以及具体出现的错误 建议联系我们WRC团队support@intersystems.com 或者电话:4006019890或者对应的客户经理,谢谢! 应该是没开启SOAP测试页能力。搜一下三级等保文章,里面有介绍 https://cn.community.intersystems.com/smartsearch?search=InterSystems+数据平台与三级等保+-+第一篇
文章
Michael Lei · 六月 26, 2022

以程序化的方式检查审计设置

下面的代码允许用户查看其实例的审计设置。通过运行类方法 "test "来运行该代码。: class objectscript.checkAudit Extends %RegisteredObject { classmethod test() { w "Checking for Auditing...",! Set SYSOBJ = ##class(Security.System).%OpenId("SYSTEM") If +SYSOBJ = 0 Set SYSOBJ = ##class(Security.System).%New() i SYSOBJ.AuditEnabled { w "Security Auditing is enabled for the following services",! s rs=##class(%ResultSet).%New("Security.Events:ListAllSystem") s sc=rs.Execute() If $$$ISERR(sc) Do DisplayError^%apiOBJ(sc) Quit while rs.%Next() { d:rs.Data("Enabled")="Yes" rs.%Print() } d rs.Close() s rs=##class(%ResultSet).%New("Security.Events:ListAllUser") s sc=rs.Execute() If $$$ISERR(sc) Do DisplayError^%apiOBJ(sc) Quit while rs.%Next() { d:rs.Data("Enabled")="Yes" rs.%Print() } d rs.Close() } } } 这是 GitHub的链接: https://github.com/intersystems-community/code-snippets/blob/master/src/cls/objectscript/checkAudit.cls
文章
姚 鑫 · 三月 8, 2021

第五章 SQL定义表(三)

# 第五章 SQL定义表(三) # 使用DDL定义表 可以使用标准DDL命令在InterSystems SQL中定义表: InterSystems SQL中可用的DDL命令 - `ALTER`命令 `ALTER TABLE`,`ALTER VIEW` - `CREATE` 命令 `CREATE TABLE`,`CREATE VIEW`,`CREATE INDEX`,`CREATE TRIGGER` - `DROP` 命令 `DROP TABLE`,`DROP VIEW`,`DROP INDEX`,`DROP TRIGGER` 可以通过多种方式执行DDL命令,包括: - 使用动态SQL。 - 使用嵌入式SQL。 - 使用DDL脚本文件。 - 使用ODBC调用。 - 使用JDBC调用。 ## 在嵌入式SQL中使用DDL 在ObjectScript方法或例程中,可以使用嵌入式SQL来调用DDL命令。 例如,以下方法创建一个`Sample.Employee`表: ```java /// d ##class(PHA.TEST.SQL).CreateTable() ClassMethod CreateTable() As %String { &sql(CREATE TABLE Sample.Employee ( EMPNUM INT NOT NULL, NAMELAST CHAR (30) NOT NULL, NAMEFIRST CHAR (30) NOT NULL, STARTDATE TIMESTAMP, SALARY MONEY, ACCRUEDVACATION INT, ACCRUEDSICKLEAVE INT, CONSTRAINT EMPLOYEEPK PRIMARY KEY (EMPNUM))) IF SQLCODE=0 {WRITE "Table created" RETURN "Success"} ELSEIF SQLCODE=-201 {WRITE "Table already exists" RETURN SQLCODE} ELSE {WRITE "Serious SQL Error, returning SQLCODE" RETURN SQLCODE_" "_%msg} } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).CreateTable() Table already exists ``` 调用此方法时,它将尝试创建`Sample.Employee`表(以及相应的`Sample.Employee`类)。如果成功,则将`SQLCODE`变量设置为0。如果失败,则`SQLCODE`包含指示错误原因的SQL错误代码。 这样的DDL命令失败的最常见原因是: - `SQLCODE -99`(违反权限):此错误表明没有执行所需DDL命令的权限。通常,这是因为应用程序尚未确定当前用户是谁。可以使用`$SYSTEM.Security.Login()`方法以编程方式执行此操作: ```java DHC-APP>w $SYSTEM.Security.Login("yx","123456") 0 ``` `SQLCODE -201`(表或视图名称不是唯一的):此错误表明正在尝试使用已经存在的表的名称创建新表。 ## 使用类方法执行DDL 在ObjectScript中,可以使用`Dynamic SQL%SQL.Statement`对象使用`Dynamic SQL`准备和执行DDL命令。 下面的示例定义了一个使用动态SQL创建表的类方法: ```java ClassMethod DefTable(user As %String,pwd As %String) As %Status [Language=objectscript] { DO ##class(%SYSTEM.Security).Login(user,pwd) SET myddl=2 SET myddl(1)="CREATE TABLE Sample.MyTest " SET myddl(2)="(NAME VARCHAR(30) NOT NULL,SSN VARCHAR(15) NOT NULL)" SET tStatement=##class(%SQL.Statement).%New() SET tStatus=tStatement.%Prepare(.myddl) IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT} SET rset=tStatement.%Execute() IF rset.%SQLCODE=0 {WRITE "Created a table"} ELSEIF rset.%SQLCODE=-201 {WRITE "table already exists"} ELSE {WRITE "Unexpected error SQLCODE=",rset.%SQLCODE} } ``` 与嵌入式SQL示例一样,如果当前没有用户登录,则此方法将失败。 ## 通过导入和执行DDL脚本定义表 可以使用`IRIS()`方法从终端会话中交互式地导入InterSystems SQL DDL脚本文件,也可以使用`DDLImport(“ IRIS”)`方法作为后台作业来导入InterSystems SQL DDL脚本文件。此方法可以导入和执行多个SQL命令,使可以使用txt脚本文件来定义表和视图,并用数据填充它们。 如果要将表从另一供应商的关系数据库迁移到InterSystems IRIS,则文本文件中可能包含一个或多个DDL脚本。 InterSystems IRIS提供了几种`%SYSTEM.SQL`方法来帮助将此类表加载到InterSystems IRIS中。可以使用通用的`DDLImport()`方法或特定供应商的`%SYSTEM.SQL`方法。供应商特定的SQL转换为InterSystems SQL并执行。错误和不支持的功能记录在日志文件中。 例如,从ObjectScript命令行加载一个Oracle DDL文件: 1. 使用InterSystems IRIS启动器菜单中的“终端”命令启动终端会话。 2. 切换到希望在其中加载表定义的名称空间: ```java SET $namespace = "MYNAMESPACE" ``` 3. 调用所需的DDL导入方法: ```java DO $SYSTEM.SQL.Oracle() ``` 并按照终端上显示的说明进行操作。 ![image](/sites/default/files/inline/images/1_23.png) # 定义分片表 创建分片表有三个要求。 1. 许可证密钥必须支持分片。使用管理门户,系统管理,许可,许可证密钥显示当前许可证或激活新许可证。 2. 必须在IRIS实例上启用分片。必须具有`%Admin_Secure`特权才能启用分片。使用“管理门户”,“系统管理”,“配置”,“系统配置”,“分片配置”来选择“启用分片”按钮。这使当前的InterSystems IRIS实例可以在分片群集中使用。选择“为任何角色启用此实例”或“仅对碎片主机角色启用此实例”。按确定。重新启动您的InterSystems IRIS实例。 3. 必须在IRIS实例上部署分片群集。此分片群集包含一个分片主名称空间。如果未为分片配置当前名称空间,则尝试定义分片表失败,并显示错误#9319:当前名称空间%1没有配置分片。 然后,可以在Shard Master命名空间中定义一个分片表,该表已定义为分片集群的一部分。可以使用`CREATE TABLE`通过指定分片键来定义分片表。或者,可以创建一个持久化类,该持久化类投影到分片表。 # 通过查询现有表定义表 可以使用`$SYSTEM.SQL.QueryToTable()`方法基于一个或多个现有表来定义和填充新表。指定一个查询和一个新的表名称。现有表名和/或新表名可以是合格的或不合格的。该查询可以包含`JOIN`语法。该查询可以提供列名别名,这些别名将成为新表中的列名。 1. `QueryToTable()`复制现有表的DDL定义,并为其指定指定的新表名。它复制查询中指定的字段的定义,包括数据类型,`maxlength`和`minval / maxval`。它不复制字段数据约束,例如默认值,必需值或唯一值。它不会将引用从字段复制到另一个表。 如果查询指定`SELECT *`或`SELECT%ID`,则将原始表的`RowID`字段复制为数据类型为整数的非必需,非唯一数据字段。 `QueryToTable()`为新表生成唯一的`RowID`字段。如果复制的`RowID`名为`ID`,则生成的`RowID`名为`ID1`。 `QueryToTable()`为此新表创建一个对应的持久化类。持久类定义为DdlAllowed。新表的所有者是当前用户。 不管源表中的这些设置如何,新表都将使用`Default Storage = YES`定义,并且`Supports Bitmap Indices = YES`。 为新表创建的唯一索引是`IDKEY`索引。没有位图范围索引生成。复制字段的索引定义不会复制到新表中。 2. `QueryToTable()`然后使用查询选择的字段中的数据填充新表。它将表格的“范围大小”设置为100,000。它估计`IDKEY`块计数。运行“音调表”以设置实际的“范围大小”和“块计数”,以及每个字段的“选择性”和“平均字段大小”值。 `QueryToTable()`既创建表定义,又用数据填充新表。如果只希望创建表定义,请在查询`WHERE`子句中指定一个不选择任何数据行的条件。例如,`WHERE Age < 20 AND Age > 20`. 下面的示例从S`ample.Person`复制`“名称”`和`“年龄”`字段,并创建一个AVG(Age)字段。这些字段定义用于创建名为`Sample.Youth`的新表。然后,该方法`where Age < 21`. 的那些记录的`Sample.Person`数据填充`Sample.Youth`。`AvgInit`字段包含创建表时所选记录的合计值。 ```java DO $SYSTEM.SQL.QueryToTable("SELECT Name,Age,AVG(Age) AS AvgInit FROM Sample.Person WHERE Age < 21","Sample.Youth",1,.errors) ``` ```java DHC-APP> DO $SYSTEM.SQL.QueryToTable("SELECT Name,Age,AVG(Age) AS AvgInit FROM Sample.Person WHERE Age < 21","Sample.Youth",1,.errors) Preparing query... Creating class... Compiling class... Copying data... ``` ![image](/sites/default/files/inline/images/2_13.png) # 外部表 在InterSystems SQL中,还可以具有“外部表”,这些表在SQL词典中定义但存储在外部关系数据库中。外部表的行为就像它们是本机InterSystems IRIS表一样:可以对它们发出查询并执行`INSERT`,`UPDATE`和`DELETE`操作。 InterSystems SQL网关提供对外部数据库的访问,该网关使用ODBC或JDBC提供透明的连接。 # List表 `INFORMATION.SCHEMA.TABLES`持久类显示有关当前名称空间中所有表(和视图)的信息。它提供了许多属性,包括模式和表名称,表的所有者以及是否可以插入新记录。 `TABLETYPE`属性指示它是基表还是视图。 以下示例返回当前名称空间中所有表和视图的表类型,架构名称,表名称和所有者: ```java SELECT Table_Type,Table_Schema,Table_Name,Owner FROM INFORMATION_SCHEMA.TABLES ``` ![image](/sites/default/files/inline/images/3_11.png) `INFORMATION.SCHEMA.CONSTRAINTTABLEUSAGE`持久类为为当前名称空间中的每个表定义的每个主键(显式或隐式),外键或唯一性约束显示一行。 `INFORMATION.SCHEMA.KEYCOLUMNUSAGE`为定义为当前名称空间中每个表的这些约束之一的一部分的每个字段显示一行。 # 列出列名和数字 可以通过以下四种方式列出指定表的所有列名(字段名): - `GetColumns()`方法。这列出了所有列名和列号,包括隐藏的列。 `ID(RowID)`字段可以隐藏也可以不隐藏。 `x__classname`列始终是隐藏的;除非使用`Final class`关键字定义了持久类,否则它将自动定义。 - 管理门户网站SQL界面(系统资源管理器,SQL)架构内容的“目录详细信息”选项卡。它列出了所有列名和列号(包括隐藏的列)以及其他信息,包括数据类型和指示列是否被隐藏的标志。 - `SELECT TOP 0 * FROM`表名。这将按列号顺序列出所有非隐藏的列名。请注意,由于隐藏的列可以按列号顺序出现在任何位置,因此您无法通过计算这些非隐藏的列名来确定列号。 - `INFORMATION.SCHEMA.COLUMNS`持久类为当前名称空间中每个表或视图中的每个非隐藏列列出一行。 `INFORMATION.SCHEMA.COLUMNS`提供了大量属性,用于列出表和视图列的特征。请注意,`ORDINALPOSITION`与列号不同,因为不计算隐藏字段。 `GetColumns()`方法同时计算隐藏字段和非隐藏字段。 下面的示例使用`INFORMATION.SCHEMA.COLUMNS`列出一些列属性: ```java SELECT TABLE_NAME,COLUMN_NAME,ORDINAL_POSITION,DATA_TYPE,CHARACTER_MAXIMUM_LENGTH, COLUMN_DEFAULT,IS_NULLABLE,UNIQUE_COLUMN,PRIMARY_KEY FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='Sample' ``` ![image](/sites/default/files/inline/images/4_7.png) ## GetColumns()方法 要以列号顺序列出表中的列名,可以使用`GetColumns()`方法,如下所示: ```java /// d ##class(PHA.TEST.SQL).GetColumn() ClassMethod GetColumn() { SET stat=##class(%SYSTEM.SQL).GetColumns("Sample.Person",.byname,.bynum) IF stat=1 { SET i=1 WHILE $DATA(bynum(i)) { WRITE "name is ",bynum(i)," col num is ",i,! SET i=i+1 } } ELSE { WRITE "GetColumns()无法找到指定的表" } } ``` `GetColumns()`列出所有已定义的列,包括隐藏的列。如果表引用了嵌入式`%SerialObject`类,则`GetColumns()`首先列出持久性类中的所有列,包括引用`%SerialObject`的属性,然后列出所有`%SerialObject`属性。在下面的`GetColumns()`结果中显示了这一点: ```java DHC-APP>d ##class(PHA.TEST.SQL).GetColumn() name is ID col num is 1 name is Age col num is 2 name is DOB col num is 3 name is FavoriteColors col num is 4 name is Home col num is 5 name is Name col num is 6 name is Office col num is 7 name is SSN col num is 8 name is Spouse col num is 9 name is x__classname col num is 10 name is Home_City col num is 11 name is Home_State col num is 12 name is Home_Street col num is 13 name is Home_Zip col num is 14 name is Office_City col num is 15 name is Office_State col num is 16 name is Office_Street col num is 17 name is Office_Zip col num is 18 ``` 还可以使用此方法确定指定列名的列号,如下所示: ```java /// d ##class(PHA.TEST.SQL).GetColumn1() ClassMethod GetColumn1() { SET stat=##class(%SYSTEM.SQL).GetColumns("Sample.Person",.byname) IF stat=1 { WRITE "Home_State is column number ",byname("Home_State"),! } ELSE { WRITE "GetColumns()无法找到指定的表" } } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).GetColumn1() Home_State is column number 12 ```
公告
Michael Lei · 一月 9, 2022

2021 年英文社区Top 问题

a {color:#2a2e78;} 嘿,社区。 这里是2021年度开发社区问题摘要。 让我们来看看InterSystems开发人员提出的最受欢迎的问题。 统计 ✓ 2021年提出了980 个问题✓ 社区总共提出了5,699 问题 最多浏览问题 WINDOWS ODBC CACHE 驱动 by Fernando Zañartu 2,113 SOAP 错误 CONTENT-TYPE 返回 text/html 而不是 text/xml by Kurro Lopez 741 如何用xmlns 和 xsi:type 属性解析xml by water huang 733 ODBC 驱动 by Brian Bechard 667 VS Code中的意外Token错误 by David Hockenbroch 646 Log4j 脆弱性 by JOSE PALAU 597 如何在SQL查询中获得行编号? by Anderson Negreli 541 httpRequest POST 文件上传by Emanuel Lazar 527 Log4Shell Apache 影响 / Intersystems产品by Andy Stobirski 486 Intersystems Cache Studio 下载 by Joseph Lovato 461 如何在httpResponse对象中设置HTTP Response 状态编码 by Mike Yackanich 441 JSON解析空值null values in by Lucas Macori 398 如何快速简单从老的Dot Scoping转化为新的Parentleses Scoping? by Dominic Chui 397 从Caché 数据库到 IRIS数据库的转化. by Сергей Марушко 383 JSON Web Token令牌 授权与不记名令牌Tokensby Neil Thaiss 371 在另一个命名空间中调用类的方法 by Nigel Salm 371 超出License 限制? by yeung elijah 364 锁 /解锁 by Matjaz Murko 352 JWT/OAuth2.0 by M C 344 转化 ISO-8859-1 输入文件 by Michoel Reach 332 显示所有英文社区问题 » 显示所有中文社区问题 » 讨论最多的 Debugging功能不见了 by Anna Golitsyna 24 在 %SYS 命名空间外获得用户属性 by Evgenii Ermolaev 22 从 VS studio终端运行Python脚本 by Akshay Pandey 20 VS Code中的意外Token错误 by David Hockenbroch 20 JSON对象上的SQL 搜索索引. by Güvenal 20 锁 /解锁 by Matjaz Murko 19 IRIS - 原生 API 和 .NET Provider - 回归到 Cache .net Provider ?by Emanuel Lazar 18 &sql(.....) 不工作且返回SQLCODE -400 by prashanth ponugoti 18 修改 %Stream 内容 by Marlin Mixon 17 收到错误 #9406 by Rick Prichett 16 在VSCode转化 ObjectScript格式 by Julian Matthews 16 从Caché 数据库到 IRIS数据库的转化. by Сергей Марушко 15 Ensemble用SQL批量插入 by Jimmy Christian 14 #5003 没有实施by Gary Koester 14 动态 SQL 参数化 UPDATE vs INSERT的问题 by Jonathan Anglin 14 如何区分命名空间 Globals 和 Routines 数据库 ? by Muhammad Waseem 14 如何测试从虚拟文档到FLR的转化 by Werner Beukes 14 第一个REST 操作 - 定制Header by Scott Roth 14 vscode 新 routines 没有显示 by Paul Price 14 显示 %GlobalCharacterStream by Rochdi Badis 13 显示所有英文社区问题 » 显示所有中文社区问题 »
文章
Weiwei Gu · 八月 14, 2023

调试管理门户加载图片失败的原因

在提交的 WRC case中(Intersystems 全球技术支持响应中心),我们经常看到客户提出有关新 Web 网关设置的问题,其中管理门户加载一半,但不显示图像。本文将解释为什么会出现此错误,以及如何修复它。本说明主要针对服务 InterSystems IRIS 实例的 Web 网关,但相同的说明也应适用于服务 Caché 实例的 CSP 网关。 问题: 您刚刚在独立的 Web 服务器上安装了 Web Gateway。当你去加载管理门户时,你发现它无法显示或加载图像,如下所示: 为什么会发生这种情况: 问题是,为了完整加载管理门户,InterSystems IRIS 必须加载许多 .js、.css 和 .png 文件(静态文件)。如果您看到像上面这样的管理门户页面,请随时打开浏览器的开发人员工具小程序,导航到“网络”选项卡,并确认未提供各种 .js、.css 和 .png 组件: 最初安装 Web Gateway 时,我们仅为以下扩展设置映射: .csp .cls .zen .cxw 这些是客户在自己的自定义应用程序中最常使用的文件扩展名类型,以及用于为 Web Gateway 管理门户提供服务的 .cxw 扩展名。如果您想要加载其他管理门户组件,则必须注册其他文件类型以由 Web 网关提供服务。 如何解决该问题: 要使管理门户完全显示,您必须配置 Web 网关以提供其他文件类型。对于 IIS,您可以为 .js、.png、.css 等扩展名添加单独的映射,也可以添加通配符映射。可以在此处找到有关注册 IIS 的其他文件类型的文档:https: //docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls ?KEY=GCGI_win#GCGI_registering 如果您在 Unix / Linux 系统上的 Apache 之上运行 Web Gateway,您有几个选择。您可以通过添加其他文件扩展名(如 IIS 上的情况)来配置此功能,也可以添加 CSP 位置指令。请参阅此处的文档了解完整详细信息: https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls ?KEY=GCGI_ux,位于“使用 CSP 注册其他文件类型”下
文章
Hao Ma · 一月 15, 2021

IAM实践指南——OAuth 2.0下的API保卫战(第一部分)

介绍 目前,诸多应用程序通过开放授权框架(OAuth)来安全、可靠、高效地访问各种服务中的资源。InterSystems IRIS目前已兼容OAuth 2.0框架。事实上社区有一篇关于OAuth 2.0和InterSystems IRIS的精彩文章,链接如下。 然而,随着API管理工具的出现,一些组织开始将其用作单点身份验证,从而防止未经授权的请求到达下游服务,并将授权/身份验证复杂性从服务本身分离出来。 您可能知道,InterSystems已经推出了自己的API管理工具,即InterSystems API Management (IAM),以IRIS Enterprise license(IRIS Community版本不含此功能)的形式提供。这里是社区另一篇介绍InterSystems AIM的精华帖。 这是三篇系列文章中的第一篇,该系列文章将展示如何在OAuth 2.0标准下使用IAM简单地为IRIS中的未经验证的服务添加安全性。 第一部分将介绍OAuth 2.0相关背景,以及IRIS和IAM的初始定义和配置,以帮助读者理解确保服务安全的整个过程。 本系列文章的后续部分还将介绍两种使用IAM保护服务的可能的场景。在第一种场景中,IAM只验证传入请求中的访问令牌,如果验证成功,则将请求转发到后端。在第二种场景中,IAM将生成一个访问令牌(充当授权服务器)并对其进行验证。 因此,第二篇将详细讨论和展示场景1中的配置步骤,第三篇将讨论和演示场景2中的配置以及一些最终要考虑的因素。 如果您想试用IAM,请联系InterSystems销售代表。 OAuth 2.0背景 每个OAuth 2.0授权流程基本上都由4个部分组成: 用户 客户端 授权服务器 资源所有者 简单起见,本文使用“资源所有者密码凭证”OAuth流(可以在IAM中使用任何OAuth流)。另外,本文将不指定任何使用范围。 注意:因为资源所有者密码凭证流直接处理用户凭证,所以应该只在客户端应用程序高度受信任时使用。在大多数情况下,客户端应为第一方应用程序。 通常,资源所有者密码凭证流遵循以下步骤: 用户在客户端应用程序中输入凭证(如用户名和密码) 客户端应用程序将用户凭证和自身的标识(如客户端ID和客户端密钥)一起发送到授权服务器。授权服务器验证用户凭证和客户端标识,并返回访问令牌 客户端使用令牌访问资源服务器上的资源 资源服务器首先验证收到的访问令牌,然后再将信息返回给客户端 考虑到这种情况,你可以在两种场景下使用IAM应对OAuth 2.0: IAM充当验证器,验证客户端应用程序提供的访问令牌,仅在访问令牌有效时才将请求转发给资源服务器;在这种情况下,访问令牌将由第三方授权服务器生成 IAM既充当授权服务器(向客户端提供访问令牌),又充当访问令牌验证器,在将请求重定向到资源服务器之前验证访问令牌。 IRIS和IAM初始定义和配置 本文中使用名为“/SampleService”的IRIS Web应用程序。从下面的截屏中可以看到,这是一个在IRIS中部署的未经身份验证的REST服务: 此外,在IAM端配置了一个名为“SampleIRISService”的服务,其包含一个路由,如以下截屏所示: 再者,在IAM中配置了一个名为“ClientApp”的客户端(初始没有任何凭据),用来识别谁在调用IAM中的API: 经上述配置,IAM将发送到以下URL的每个GET请求代理到IRIS: http://iamhost:8000/event 此时还没有使用身份验证。所以,如果将一个简单的GET请求(未进行身份验证)发送到URL: http://iamhost:8000/event/1 我们将获得期望的响应。 本文中,我们使用名为“PostMan”的应用程序发送请求并检查响应。在下面的PostMan截屏中,可以看到简单的GET请求及其响应。 请继续阅读本系列的第2篇,了解如何配置IAM来验证传入请求中的访问令牌。
文章
Hao Ma · 十一月 14, 2022

ZPM介绍(1)

# ZPM介绍 有Developer听闻了InterSystems的包管理器ZPM, 希望我能介绍一下。正好刚刚看到一个开发者论坛的新闻:[ Open Exchange ZPM is now InterSystems Package Manager ](https://community.intersystems.com/post/zpm-now-intersystems-package-manager-ipm), 觉得更有必要了。 ## zpm是什么 简单说:zpm, 全称InterSystems ObjectScript Package Manager, 是一个包管理器, 开发者是Nikolay Soloviev和Dmitry Maslennikov。它先是在开发者社区里得到认可,以至于InterSystems开发者社区最近的一些比赛,要求参赛作品用zpm打包。然后就有了上面的链接的内容:InterSystems决定把它做为自己官方的打包工具, 将它改名字叫IPM(InterSystems Package Manager),同时保持它的开源状况不变。 这里我还是用zpm称呼它。两个原因。1. 操作的命令还是zpm, 所谓ipm,只是官方给的名字。2. ipm新的注册中心还不太了解,而且**到目前为止,IPM的注册中心还只对自己的雇员和付费用户开放**。本文的读者应该都还没来得及付费,所以暂时先放放,还是叫它zpm。 考虑到没有程序员背景的读者而对包管理器不熟悉,啰嗦两句。 ### 什么是包管理器 大多数开发工具都有自己的包管理器,Java开发使用MAVEN, Python用PIP, NodeJS使用npm, 它们细节有区别,功能大致是一致的,就是安装管理程序包,最基本的工作是: 1. 找到自己要的程序包。 简单的命令能让你在网上找到它,用名字找,或者其他方法搜索。 2. 安装包。 - 这里面最重要的是发现包之间的依赖。比如你要装一个软件包叫“包饺子”,作者在开发的时候使用了另一个软件叫“和面”,这个“和面”可能是作者自己写的,或者是网上其他人写的,注册在包管理器的。这时候作者在”包饺子“里声明,我依赖”和面“。 那么,当您去下载安装"包饺子“的时候,包管理器会自动的把”和面“也给你下载并安装上。 3. 管理包: 比如升级,删除等等。 ### ZPM的特点 没有zpm的时候,人们是怎么传递ObjectScript代码的? 大概是这样:发布者把代码从studio导出成一个XML;接受者拿到这个xml, 然后使用Studio或者iris管理门户把它导入到一个命名空间。 这里面有两个问题:1. 麻烦, 2. 没有打包各种类型文件的能力。通常一个项目,除非是只使用ObjectScript, 多数都用到各种文件:前端的css, html, javascript, 图片; 各种配置文件xml或者yaml, 其他语言的包 (iris支持嵌入式python,以及集成多种编程语言工作)等等。 ZPM是怎么工作的? 1. 作者把自己的数据放在一个公网地址上,比如github; 使用zpm生成一个配置文件(module.xml); 把module.xml发布到zpm的registry, 让别人能找到你的软件包。 2. 使用者在Registry搜寻并下载这个程序包,zpm命令自动把它导入到IRIS的一个命名空间。 为什么能自动导入到iris ? 这里有个ZPM和其他包管理器的很大的区别: **ZPM命令是在IRIS的terminal里执行的, 而不是操作系统上。** 还有一个特点:**zpm的设计假设大家开发代码使用的IDE是VSCode而不是Studio**,这非常关键。Studio上的开发是在服务端的开发,没有一个类应该存在哪个文件目录的概念。VSCode相反,你创建一个类:`Demo.Web.Test`, 那么默认的文件保存是在一个这样的目录下: `./src/Dome/Web/Test.cls` 不遵循这样的目录结构,您要额外做很多手工的调整才能使用zpm保存加载程序包。 这里还有很多问题, 我们后面详细说。 ## ZPM的下载安装 您可以从github上的InterSystems-Community用户的[zpm Repo](https://github.com/intersystems-community/zpm/wiki/01.-Getting-started)下载zpm-xxx.xml文件, 其中xxx是版本号, 当前(2022年10月)最新的版本是0.4.0。 把下载的xml文件导入到IRIS的**任何**命名空间。这样安装就成功了。 要下载安装软件的时候,您要先进入安装目标的命名空间(比如下面的USER),然后输入zpm, 也就进入了ZPM的操作界面: *ZPM Shell* ```sh USER>zpm ============================================================================= || Welcome to the Package Manager Shell (ZPM). || || Enter q/quit to exit the shell. Enter ?/help to view available commands || ============================================================================= zpm:USER> ``` 输入help, 您可以看到帮助文件。(帮助文件很长, 我只贴一小段。) ```sh zpm:USER>help -v Available commands: NOTE: [] around a parameter indicates it is optional arrange [flags] [] ■ Description: Rearranges the resources in a module manifest to follow the standard format # 此处省略许多行 ........ For more detail, run: help or help -v zpm:USER> ``` 接着, 我相信您一定想看看安装命令是怎么执行的,比如这样: ```sh zpm:USER>help install install [flags] [] ...此处省略若干行... ■ Examples ∙ install HS.JSON 1.x Installs the most recent 1.x version of HS.JSON available in any configured repository in the current namespace. zpm:USER>quit USER> ``` 好吧,让我们来下载一个最受欢迎的开发者应用“webTerminal”,了解最基本的zpm操作。 ## 最基本的ZPM操作 ### 搜索package 输入`zpm:USER>search`或者, 您可以使用`zpm:USER>search -r`,“-r"显示数据包所在的repo的位置。我没有贴在这里是因为显示的内容太宽了。在我的帖子的代码框里显示看上去有点乱。 ```sh zpm:USER>zpm:USER>search registry https://pm.community.intersystems.com: alwo-goselector 1.0.1 analytics-okr 1.0.0 ...此处省略许多行... zpm-shields 1.0.1 zpmhub 0.3.1 zpmshow 1.0.3 zpm:USER> ``` 除了search, 还有个find命令。 它可以使用*,但无法忽略大小写。 ```sh zpm:USER>find *Webt* registry https://pm.community.intersystems.com: zpm:USER>find *webt* registry https://pm.community.intersystems.com: webterminal 4.9.6 zpm:USER> ``` ### 安装package ```sh zpm:USER>install webterminal ...(省略若干行)... WebTerminal package successfully mapped into all namespaces. [USER|webterminal] Compile SUCCESS [USER|webterminal] Activate START [USER|webterminal] Configure START [USER|webterminal] Configure SUCCESS [USER|webterminal] Activate SUCCESS zpm:USER> ``` **到这里, 我来再次总结一下ZPM的最基本的功能:原本您想要使用一个其他开发者使用的程序,您要把它从某处,比如一个帖子,InterSystems的OpenExchange网站,或者一个github的主页上下载程序(XML文件),然后导入到IRIS执行。有了ZPM, 你可以在IRIS的terminal里通过ZPM命令,直接获得这个程序并执行。前提是:作者的程序是用ZPM打包并放在ZPM注册中心。** 来先了解一下ZMP的注册中心 ## 注册中心(Registry) 默认安装下, zpm的注册中心是这样的 ```sh zpm:USER>repo -list registry Source: https://pm.community.intersystems.com Enabled? Yes Available? Yes Use for Snapshots? Yes Use for Prereleases? Yes Is Read-Only? No Deployment Enabled? No zpm:USER> ``` 这也是当前zpm唯一的一个注册中心(配置私服会在后面介绍), 组册的软件包是最近一些年社区开发者写的各种工具和示例代码。说一下它的当前状况: - 当前能下载到278个软件包。 - 所有的包都放在了github。 - 包都很新。 这么说吧,release能到2.0的都是凤毛麟角 这里有个让人意外的事。我安装完webterminal并开始使用的时候,浏览器跳出这个提示。 “Welcome to WebTerminal! ...New update is available. ”。这说明这个registry并没有给出最新的webterminal版本。 我有点好奇,去看了一下webterminal的github page, 发现WebTerminal当前的版本是4.9.5。而zpm registry上下载的版本是4.9.2, 发行于2019年。 webterminal是一个前台工具,作者理所当然的的在代码里做了版本更新的检查并提醒了用户。而对于纯后台的ObjectScript语言的代码, 我估计大多数程序员都做不到这一点。 那么如果要保证其他人看到的是新版的软件, 就要求软件的作者,除了更新自己Github上的代码,还要把新版本及时更新在zpm的注册中心上。 文章太长了, 先贴一部分,后面会介绍 - 软件的发布 - 定义软件包的依赖 - 配置私服 *to be continued* 期待
文章
Qiao Peng · 一月 31

用Java开发互操作产品 - PEX

InterSystems IRIS、Health Connect和上一代的Ensemble提供了优秀的互操作架构,但即便有低代码开发能力,很多开发者还是希望能用自己的技术栈语言在InterSystems的产品上开发互操作产品。 考虑到互操作产品本身的开放性要求和各个技术栈背后庞大的生态价值,InterSystems IRIS和Health Connect提供了Production EXtension (PEX)架构,让开发者使用自己的技术栈语言来开发互操作解决方案。目前PEX支持Java、.net、Python。 这里我们介绍使用Java利用PEX进行互操作产品的开发。 一 InterSystems IRIS上使用Java开发的基础 在进入PEX主题前,需要简单介绍一下Java在InterSystems IRIS上开发的各种技术选项,因为PEX也是以这些技术选项为基础的。 如果仅把InterSystems IRIS作为一个数据库看待,可以使用JDBC对它进行SQL访问,这是最传统的开发方式; Java是一个面向对象的开发语言,而InterSystems IRIS也是支持面向对象的数据平台。InterSystems IRIS的外部语言服务器技术,提供了一套Java原生SDK,让Java和InterSystems IRIS可以互相访问对方的对象,甚至Java可以通过它直接访问InterSystems IRIS后台的持久化多维数组。外部语言服务器提供了非常灵活、高效的开发方式; 如果是想把Java对象直接持久化到InterSystems IRIS,除了Hibernate,InterSystems IRIS提供一种更高性能的方法 - XEP(Express Event Persistence表达事件持久化); 最后,Java可以通过PEX开发InterSystems IRIS的互操作产品组件。PEX基于Java原生SDK。 作为多种Java访问方式的基础,InterSystems IRIS的Java外部语言服务器的架构如下: InterSystems IRIS和Java通过动态代理对象的方式操作对方对象,动态代理无需在对方代码变更时重新生成代理类。 InterSystems IRIS提供了intersystems-jdbc-<版本号>.jar,不仅用于JDBC连接、也用于Java外部语言服务器连接,提供Java原生SDK。 在Java端通过jdbc.IRIS管理InterSystems IRIS连接,使用jdbc.IRISObject反向操作IRIS对象。 InterSystems IRIS端提供并管理包括Java语言服务器在内的一系列外部语言服务器。注意,一般情况下,使用系统开箱的这些外部服务器就够了,不需要额外建立额外的Java语言服务器。而且,也无需事先启动,外部语言服务器会在调用时自动启动。 Java连接到InterSystems IRIS,并创建IRIS外部服务器的示例: String connStr = "jdbc:IRIS://127.0.0.1:1972/USER"; String user = "superuser"; String pwd = "SYS"; // 使用DriverManager 创建IRIS连接 // IRISConnection conn = (IRISConnection)java.sql.DriverManager.getConnection(connStr,user,pwd); // 或使用IRISDataSource 创建IRIS连接 IRISDataSource ds = new IRISDataSource(); ds.setURL(connStr); ds.setUser(user); ds.setPassword(pwd); IRISConnection conn = (IRISConnection) ds.getConnection(); // 创建IRIS外部服务器 IRIS irisjv = IRIS.createIRIS(conn); // 调用IRIS类%PopulateUtils的类方法Name,产生随机姓名 String tempTrader = irisjv.classMethodString("%PopulateUtils", "Name"); 二 PEX 开发 PEX针对于互操作产品的开发,通过PEX使用外部语言开发互操作产品所需的组件,甚至可以通过PEX开发互操作产品的所有组件。 2.1 InterSystems IRIS互操作产品架构 典型的InterSystems IRIS互操作产品架构如下,它的典型工作机制是: 业务服务通过入站适配器监听外部系统,例如TCP和文件系统,将数据组织成消息发送给业务流程 业务流程根据流程需要在特定流程节点将消息发送给业务操作 业务操作通过出站适配器访问外部应用,发送数据并获取响应,并将响应数据组织成消息反馈给业务流程 在互操作产品内部流转的都是消息,消息默认自动持久化并可以通过SQL等方式结构化查询 互操作产品涉及3类组件: 业务组件:包括业务服务、业务流程、业务操作 适配器:包括入站适配器和出站适配器 消息 这3类组件都可以通过PEX进行开发。 2.2 PEX 类和类包 InterSystems IRIS 提供了一个intersystems-utils-<版本号>.jar包,提供com.intersystems.enslib.pex类包,用于基于Java开发互操作产品组件。 在Java代码里还要调用IRIS Object Script开发的组件和代码,因此,还需要intersystems-jdbc-<版本号>.jar。 2.3 示例业务场景介绍 下面我们根据一个示例业务场景介绍如何使用Java PEX进行互操作产品的开发。 业务场景是一个银行间汇款的流程: 业务服务(Finance.FromFileTransaction)监听特定文件夹,并从新汇款申请文件中解析出汇款请求,发送给业务流程(Finance.ProcessPaymentRequest); 业务流程先将请求发送给汇出行的业务操作(Finance.ToPayFromBank),获取汇出行的审核结果; 如果汇出行审核批准,则业务流程继续将请求发送给接收行的业务操作(Finance.ToPayToBank) 这里汇出行的业务操作和接收行的业务操作都会将数据写入特定文件夹的文件中。 这个场景中,我们完全使用Java开发全部的组件,包括:请求、响应消息;入站、出站文件适配器;业务操作;业务流程和业务服务。 下面我们就开始吧。 2.4 消息开发 Java消息类是com.intersystems.enslib.pex.Message的子类,消息中的属性,需要定义为public。它会在互操作业务组件间传递时自动持久化。 在IRIS端,Java消息自动映射到EnsLib.PEX.Message,它是一个持久化的IRIS类,包含以下属性: %classname属性:Java消息类名 %json属性:Java消息的JSON类型(IRIS动态对象) 在InterSystems IRIS端,例如消息可视化追踪页面和消息表,你看到的Java消息都是EnsLib.PEX.Message类型。 PEX消息用到的共同账号信息类的示例代码: package Finance; // 封装银行账号信息的类. public class PaymentProfile extends com.intersystems.enslib.pex.Message { public int AccountNumber; public int RoutingNumber; public String UserName; } 请求消息的示例代码: package Finance; import Finance.PaymentProfile; // PEX开发的转账请求消息. public class TransactionRequest extends com.intersystems.enslib.pex.Message { public double TransactionAmount; // 转账金额 public PaymentProfile PayFrom; // 付方银行账号信息 public PaymentProfile PayTo; // 收方银行账号信息 public String FromCurrency; // 付方货币 public String ToCurrency; // 收方货币 // 实例化银行账号属性. public TransactionRequest(){ PayFrom = new PaymentProfile(); PayTo = new PaymentProfile(); } } 响应消息的示例代码: package Finance; // PEX开发的转账响应消息. public class TransactionResponse extends com.intersystems.enslib.pex.Message { public boolean approved; } 2.5 业务组件和适配器的运行时变量 在互操作产品的管理配置页面,可以设置各个业务组件和适配器的配置项,例如文件适配器的文件路径,方便实施和管理。 对于Java开发的业务组件和适配器,同样我们需要这样的配置项。这些配置项,我们称之为运行时变量,这样进行定义: 运行时变量是Java业务组件类和适配器类的public类型的属性 对这些属性使用Java annotation进行元数据描述,例如: @FieldMetadata(IsRequired=true,Description="文件路径") public String FilePath; 后面的Java业务组件和适配器会用到运行时变量的声明方法。 2.6 入站适配器开发 如果要开发Java入站适配器,需要定义一个com.intersystems.enslib.pex.InboundAdapter的Java子类。并在类中实现以下方法: 方法 参数 说明 OnTask 无 适配器根据调用间隔设置,周期性地调用该方法对数据源进行查询;将数据调用BusinessHost.ProcessInput()将数据发送给业务服务;BusinessHost是业务服务的固定的变量名 OnInit 无 适配器初始化的方法 OnTearDown 无 适配器退出清理的方法 例如入站文件适配器示例代码如下: package Finance; import java.io.File; // 导入File类. import java.io.FileNotFoundException; // 导入此类以处理错误. import com.intersystems.enslib.pex.FieldMetadata; // 导入PEX运行时变量类 // 这是一个PEX的入站适配器,检测定义在InboundFilePath的文件夹路径。如果文件夹下有文件,遍历这些文件,使用ProcessInput()将文件路径发送给业务服务 public class PEXInboundAdapter extends com.intersystems.enslib.pex.InboundAdapter { @FieldMetadata(IsRequired=true,Description="输入文件路径") public String InboundFilePath; // 文件夹路径配置项. public void OnTearDown() {} public void OnInit() {} public void OnTask() throws Exception { // 检查文件夹路径,并遍历文件. File folder = new File(InboundFilePath); File[] list = folder.listFiles(); if (list.length > 0) { for (File file : list) { // 忽略.keep后缀文件. // 其它文件路径发送给业务组件BusinessHost. if (file.getName().equals(".keep")) {continue;} BusinessHost.ProcessInput(file.getAbsolutePath()); } } } } 2.7 Java开发组件的注册与更新 开发好的Java组件,需要在InterSystems IRIS端进行注册,让互操作产品可以直接使用这些组件。 注册的方法在Production扩展页面,点击注册新组件: 在远程类名称中填写Java组件类名 在代理名称中填写IRIS生成的代理类名称,可以和Java类名不同。这个类名是在互操作产品中看到和使用的名字。 选择外部语言服务器为%Java Server 将封装有Java组件类的jar文件路径和其它依赖jar文件路径添加到网关额外CLASSPATHS下 注册后就可以在列表中看到注册的组件和组件类型,并在互操作产品中可以直接使用了。 如果后来修改了Java代码,则需要选中已经组册的组件,对其进行更新: * 记得下面这些Java开发的组件也需要注册后使用。 2.8 出站适配器开发 同样,可以通过继承com.intersystems.enslib.pex.OutboundAdapter来开发Java出站适配器类。 它需要实现以下方法: 方法 参数 说明 OnInit 无 适配器初始化的方法 OnTearDown 无 适配器退出清理的方法 其它方法 根据需要 执行具体的适配器操作 例如出站文件适配器示例代码如下,它提供写文件的方法: package Finance; import java.io.File; // 导入File类. import java.io.FileNotFoundException; // 导入此类以处理错误. import java.lang.StringBuffer; // 导入string buffer类. import java.io.FileWriter; // 写文件的包. import java.util.Date; // 处理日期的包. import com.intersystems.enslib.pex.FieldMetadata; // 导入PEX运行时变量类. import java.text.SimpleDateFormat; // 格式化日期字符串的包. // 这是一个PEX的出站适配器,通常被业务操作使用,连接到外部系统. 这里适配器提供WriteToFile()方法,供业务操作调用. public class PEXOutboundAdapter extends com.intersystems.enslib.pex.OutboundAdapter { @FieldMetadata(IsRequired=true,Description="输出文件路径") public String FilePath; // 输出文件路径. public void OnTearDown() {} public void OnInit() {} //写文件的方法,输入参数input是向文件中写入的内容 public Object WriteToFile(java.lang.Object input) throws Exception { // 转换为字符串 String OutputMessage = (String)input; // 允许用户输入的路径有或没有尾部路径分隔符. if (FilePath.substring(FilePath.length() - 1) != File.separator) { FilePath = FilePath + File.separator; } // 用当前日期时间产生文件名. Date today = new Date(); SimpleDateFormat Formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); String DateString = Formatter.format(today); // 产生完整文件路径. String OutputPath = FilePath + DateString; // 创建文件. File OutputFile = new File (OutputPath); // 实例化FileWriter对象. FileWriter writer = new FileWriter(OutputFile); // 将内容写入文件 writer.write(OutputMessage); writer.close(); return null; } } 2.9 业务操作开发 Java业务操作类,是com.intersystems.enslib.BusinessOperation的子类。需要实现以下功能: 方法 参数 说明 OnInit 无 适配器初始化的方法 OnTearDown 无 适配器退出清理的方法 OnMessage 无 个方法会在业务操作收到其它业务组件传递过来的消息时被调用 在该方法内部,可以调用适配器的方法 - Adapter.invoke("适配器方法名", 方法参数) Adapter是适配器对象,已经实例化,无需执行自己的实例化代码。Adapter是固定变量名 getAdapterType 无 返回使用的适配器类名 如果是PEX开发的适配器,返回的是它注册到InterSystems IRIS的代理类名 如果使用默认适配器,返回 Ens.InboundAdapter 如果不使用适配器,返回空字符串 例如我们的示例中,有以下2个业务操作: 1. 与汇出银行接口的业务操作类: package Finance; import Finance.TransactionRequest; import Finance.PaymentProfile; import Finance.TransactionResponse; import java.lang.StringBuffer; // PEX业务操作类,和支付银行接口. // 它从业务流程接收请求消息Finance.ProcessPaymentRequest, 将消息内容写入文件并返回内容为'approval' 的响应消息(Finance.TransactionResponse). public class ToPayFromBank extends com.intersystems.enslib.pex.BusinessOperation { public void OnTearDown() {} public void OnInit(){} // 确定适配器 public String getAdapterType() { return "Finance.PEXOutboundAdapter"; } // 响应请求消息 public java.lang.Object OnMessage(java.lang.Object object) throws java.lang.Exception { // 将输入对象转换为对应的类实例. TransactionRequest request = (TransactionRequest)object; // 初始化StringBuffer对象,构造输出文件内容. StringBuffer outputMessage = new StringBuffer(); // 将数据写入StringBuffer. outputMessage.append("Debit request:" + System.lineSeparator()); outputMessage.append("Route:" + request.PayFrom.RoutingNumber + System.lineSeparator()); outputMessage.append("Account: " + request.PayFrom.AccountNumber + System.lineSeparator()); outputMessage.append("Amount: " + request.TransactionAmount); // 调用出站适配器的方法,将数据写入文件. Adapter.invoke("WriteToFile", outputMessage.toString()); // 实例化响应消息,并对响应消息赋值后返回给业务流程. TransactionResponse response = new TransactionResponse(); response.approved = true; return response; } } 2. 与接收银行接口的业务操作类: package Finance; import Finance.TransactionRequest; import Finance.PaymentProfile; import java.lang.StringBuffer; // PEX业务操作类,和收款银行接口. // 从业务流程接收请求消息,并将消息内容写入文件. public class ToPayToBank extends com.intersystems.enslib.pex.BusinessOperation { public void OnTearDown() {} public void OnInit(){} // 确定适配器 public String getAdapterType() { return "Finance.PEXOutboundAdapter"; } // 响应请求消息 public java.lang.Object OnMessage(java.lang.Object object) throws java.lang.Exception { // 将输入对象转换为对应的消息类实例. TransactionRequest request = (TransactionRequest)object; // 实例化StringBuffer对象以构造文件内容. StringBuffer outputMessage = new StringBuffer(); // 将数据写入StringBuffer. outputMessage.append("credit request:" + System.lineSeparator()); outputMessage.append("SourceRouting:" + request.PayFrom.RoutingNumber + System.lineSeparator()); outputMessage.append("SourceAccount: " + request.PayFrom.AccountNumber + System.lineSeparator()); outputMessage.append("ToAccount: " + request.PayTo.AccountNumber + System.lineSeparator()); outputMessage.append("Amount: " + request.TransactionAmount); // 调用出站适配器方法将数据写入文件 Adapter.invoke("WriteToFile", outputMessage.toString()); // 无需响应消息. return null; } } 2.10 业务流程开发 使用Java开发业务流程,则完全是代码开发模式,而非基于BPL的低代码模式。 Java业务流程类是com.intersystems.enslib.pex.BusinessProcess 的子类,需要实现以下方法: 方法 参数 说明 OnInit 无 适配器初始化的方法 OnTearDown 无 适配器退出清理的方法 OnRequest request 请求消息 处理发送给业务流程的请求消息,这个请求消息启动业务流程 OnResponse request 请求消息, response 响应消息, callRequest - 本次异步调用发起的请求消息, callResponse - 本次异步调用收到的响应消息, completionKey - 异步调用发起时传入的completionKey 处理异步调用返回的响应消息-callResponse OnComplete request 请求消息, response 响应消息 整个业务流程完成时,被调用的方法 另外,类里还可以声明需要在业务流程期间保存的持久化数据,和在BPL中要到的上下文(context)数据一样,例如记录汇出行的审核结果。声明时,需要加@Persistent这个Java annotation: @Persistent // 定义名为runningTotal的持久化的属性 public integer runningTotal = 0; 本用例的业务流程示例代码如下: package Finance; import Finance.TransactionRequest; //导入请求消息类 import Finance.TransactionResponse; //导入响应消息类 // 这是一个PEX业务流程类,它从业务服务(Finance.FromFileService)接收请求消息. // 先将汇款请求消息发送给汇出行业务操作(Finance.ToPayFromBank) 并等待审核结果. 如果批准, 将请求消息发送给接收行业务操作(Finance.ToPayToBank). public class ProcessPaymentRequest extends com.intersystems.enslib.pex.BusinessProcess { public Object OnComplete(Object object,Object object2) {return null;} public void OnTearDown(){} public void OnInit(){} // 无需返回响应消息给调用者 public java.lang.Object OnResponse(java.lang.Object request, java.lang.Object response, java.lang.Object callRequest, java.lang.Object callResponse, java.lang.String completionKey) throws java.lang.Exception { return null; } // 处理请求消息 public java.lang.Object OnRequest(java.lang.Object object) throws java.lang.Exception { // 将输入对象转换为对应的消息类实例. TransactionRequest request = (Finance.TransactionRequest) object; // 将支付请求消息以同步请求的方式发送给汇出行. Object response = SendRequestSync("Finance.ToPayFromBank", request); // 将收到的响应消息对象转换为对应的消息类实例. TransactionResponse bankResponse = (TransactionResponse)response; // 记录汇出行审核结果,用于调试 LOGINFO("汇出行审核结果是:"+bankResponse.approved); // 检查汇出行审核结果,如果是批准,则将汇款请求发送给接收行. if (bankResponse.approved) { // 使用异步调用方式通知接收行,无需等待返回. SendRequestAsync("Finance.ToPayToBank", request); } return null; } } 2.11 业务服务开发 Java业务服务类是com.intersystems.enslib.pex.BusinessService 的子类。它有一个父类定义的属性 – TargetComponentNames, 用于设置发送消息目标的下游业务组件名列表。可以同时发送消息给多个下游业务组件,组件名之间通过逗号分隔。 业务服务可以使用适配器,这时它会根据适配器的设置定期轮询外部系统,例如SQL适配器轮询外部SQL表、文件适配器轮询外部文件夹。但业务服务也可以不使用适配器,而直接被外部系统调用,例如发布为SOAP、RESTful服务的业务服务就不使用适配器。 适配器 执行过程 需要实现的方法 使用专业适配器 适配器的OnTask()方法定期执行获得数据,将数据发送给业务服务的ProcessInput() OnProcessInput() 使用默认适配器 Ens.InboundAdapter 适配器的OnTask()方法定期执行,并调用业务服务的ProcessInput(); 业务服务的OnProcessInput()执行数据获取 OnProcessInput() 不使用适配器 不定期执行,用户代码在需要时通过Director.CreateBusinessService() 创建业务服务,并执行其方法 OnProcessInput() 或其它需要的方法 本示例中的业务服务使用前面Java开发的文件入站适配器,其代码如下: package Finance; import java.io.File; // 导入File类. import java.io.FileNotFoundException; // 导入此类以处理错误. import java.util.Scanner; // 导入Scanner类读取文本文件. import Finance.TransactionRequest; // 导入请求消息类. // 这是一个PEX的业务服务类,从文件读取汇款请求. 将文件内容转换为Finance.TransactionRequest消息对象,并检查运行时变量TargetComponentNames,确定需要把请求消息发送给哪些业务组件. public class FromFileTransaction extends com.intersystems.enslib.pex.BusinessService { public String TargetComponentNames; // 用逗号分隔的发送目标组件列表,运行时从配置项获取. public void OnTearDown() {} public void OnInit() {} // 确定适配器 public String getAdapterType() { return "Finance.PEXInboundAdapter"; } // OnProcessInput根据业务服务的'call interval' 设置会被定期调用. public java.lang.Object OnProcessInput(java.lang.Object messageInput) throws java.lang.Exception { String path = (String)messageInput; File file = new File(path); // 创建scanner对象读取文件. Scanner reader = new Scanner(file); // 实例化请求消息TransactionRequest对象. TransactionRequest request = new TransactionRequest(); // 设置消息属性. request.TransactionAmount = Float.parseFloat(reader.nextLine().split(":")[1]); String tempString = reader.nextLine(); // 解析嵌套的PaymentProfile对象,并赋值给请求消息的PayFrom属性. String[] tempStringArray = tempString.split(":"); String[] PaymentProfile = tempStringArray[1].split("\\|"); request.PayFrom.AccountNumber = Integer.parseInt(PaymentProfile[0]); request.PayFrom.RoutingNumber = Integer.parseInt(PaymentProfile[1]); request.PayFrom.UserName = PaymentProfile[2]; // 解析PaymentProfile对象,并赋值给PayTo属性. tempString = reader.nextLine(); tempStringArray = tempString.split(":"); PaymentProfile = tempStringArray[1].split("\\|"); request.PayTo.AccountNumber = Integer.parseInt(PaymentProfile[0]); request.PayTo.RoutingNumber = Integer.parseInt(PaymentProfile[1]); // 设置剩余属性. request.PayTo.UserName = PaymentProfile[2]; request.FromCurrency = reader.nextLine().split(":")[1]; request.ToCurrency = reader.nextLine().split(":")[1]; reader.close(); // 处理后删除文件. file.delete(); // 获取Split target business component string and send to each component. String[] targetNames = TargetComponentNames.split(","); for (String name : targetNames){ SendRequestAsync(name, request); } return null; } } 2.12 组装互操作产品并进行测试 将这些Java开发的组件组册后,就可以像InterSystems IRIS的本地组件一样使用了。我们将业务操作、业务流程和业务服务依次加入到Production,并配置好相应的配置项: 对于每个业务操作,需要配置一项: 文件路径:输出的文件夹路径 对于业务服务,需要配置两项: InboundFilePath: 监听的汇款请求文件夹路径 TargetComponentNames: 目标业务流程,这里应该填Finance.ProcessPaymentRequest 下面是用于测试的汇款请求文件内容,把它copy到一个文本文件中保存: TransactionAmount:59.43 PayFrom:232422|23532532|TJohnson21 PayTo:24224242|423533453|ERichards55 FromCurrency:USD ToCurrency:USD 启动Production后,可以将测试数据文件copy到业务服务监听到文件夹下。如果成功,应该可以看到这样的消息追踪结果,可以看到,用Java开发的PEX组件运行结果和IRIS自带的组件没有区别: 2.13 需要实现的Java类方法总结 下图总结了用Java开发不同组件时,需要实现的方法,以及这些组件间的调用流程和关系: 完整的规范参见这里。 三 在Java组件中使用Object Script开发的组件 上面是使用Java开发全套互操作产品组件的示例。实际情况下,InterSystems IRIS已经提供了大量适配器、消息和业务组件,而且使用低代码工具 - 例如业务流程建模图形化工具 - 往往更方便,所以通常Java可以使用Object Script开发的大量组件,而无需全部由Java开发。 在低代码开发业务流程时,可以直接在BPL里调用Java PEX开发的业务流程和业务操作,和IRIS本地开发的组件无异。 另外两种典型的用例是Java组件使用Object Script的消息、Java组件使用Object Script的适配器。在Java端使用Object Script类和对象,需要有前面讲的Java外部语言服务器的基础,文档参见这里。 3.1 Java组件使用Object Script的消息 在Java PEX中使用Object Script定义的消息,直接使用IRIS类的反向代理类IRISObject即可。例如在Java PEX业务操作的OnMessage方法中,要获得传入的Object Script消息的属性值,只需要把传入消息对象转换为IRISObject对象,并调用它的getXXX方法获取属性即可,可以这样: @Override public Object OnMessage(Object object) throws Exception { // 处理类型Ens.StringRequest的ObjectScript消息,获取StringValue属性的值 // 使用ObjectScript消息,将Object转换为 IRISObject IRISObject requestObj = (IRISObject)object; 通过IRISObject的getString方法获取它名为StringValue属性的值 String value = requestObj.getString("StringValue"); ... } 那怎么在Java组件中创建新的Object Script消息实例呢?你需要已经建立了IRIS连接的类型为com.intersystems.jdbc.IRIS的实例。 PEX的Java业务组件类和适配器类都继承于Common类,从Common类继承一个公共属性irisHandle,类型为IRISObject。而irisHandle有一个公共属性iris,类型为com.intersystems.jdbc.IRIS。因此可以通过它来创建Object Script消息实例。例如我们要创建一个类型为Ens.StringContainer的响应消息,并给其StringValue属性赋值: @Override public Object OnMessage(Object object) throws Exception { // 处理类型Ens.StringRequest的ObjectScript消息,获取StringValue属性的值 // 使用ObjectScript消息,将Object转换为 IRISObject IRISObject requestObj = (IRISObject)object; String value = requestObj.getString("StringValue"); // 创建一个类型为Ens.StringContainer的ObjectScript响应消息 IRISObject response = (IRISObject)(this.irisHandle.iris.classMethodObject("Ens.StringContainer","%New")); response.set("StringValue",value+"test me"); return response; } 3.2 Java业务组件使用Object Script的适配器 如果Java业务服务使用Object Script的适配器,无需做任何处理,适配器会将数据发送给Java业务服务的OnProcessInput方法,而Java业务服务只需要将接收的数据转换为IRISObject类型进行处理即可。 如果是Java业务操作要使用Object Script适配器,则需要调用适配器的方法。调用适配器方法,可以使用Adapter属性的invoke方法调用适配器类的方法,语法是 Adapter.invoke("方法名", 方法参数),例如: // 使用Object Script的出站文件适配器(EnsLib.File.OutboundAdapter),并调用它的Exists方法检查是否存在文件a.txt Object obj = Adapter.invoke("Exists", "a.txt"); 虽然这里介绍的是使用Java PEX开发互操作产品组件,您也可以使用.net 和Python进行开发,而且任何语言开发的PEX组件都可以一起使用。通过PEX,可以充分利用已有的生态代码和组件快速开发互操作产品。
文章
Hao Ma · 十一月 22, 2023

访问 IRIS 终端:Visual Studio Code 用户综合指南

介绍 由于InterSystems最近宣布从2023.2版本开始停止对InterSystems Studio的支持,转而独家开发Visual Studio Code(VSC)IDE的扩展,相信后者比Studio提供了更优越的体验,我们很多开发者都已切换或开始使用 VSC。很多人可能想知道如何打开终端进行操作,因为VSC没有像Studio那样的输出面板,也没有集成的功能来打开IRIS终端,除非下载InterSystems开发的插件。 概括 介绍 解决方案 对于至少具有 IRIS 2020.1 或 IRIS IRIS 2021.1.2 的用户– 使用 Web 终端 对于至少具有 IRIS 2023.2 的用户 – 使用 WebSocket 终端 对于使用基于 Docker 的 IRIS 的用户 对于在本地计算机上使用 2023.2 之前的 IRIS 版本的用户 对于使用 SSH 连接在基于远程服务器的 IRIS 上进行编码的用户 解决方案 在 VSC 中打开终端的方法有多种,具体取决于您使用的具体配置,我在这里总结了适合任何情况的最佳解决方案: 对于至少具有 IRIS 2020.1.1 或 IRIS 2021.1.2 的用户 – 使用 Web 终端 至少拥有 IRIS 2020.1.1 或 IRIS 2021.1.2 且被允许安装外部扩展的用户(某些人可能由于公司有关第三方应用程序的政策而不允许安装),可能会发现 VSC 的 Web 终端扩展很有用。谁不知道, Web 终端是一个基于 Web 的 InterSystems 产品终端,使用 ObjectScript(例如 IRIS、Caché、Ensemble、HealthShare、TrakCare)构建,允许在浏览器中使用更高级版本的终端( 这里是项目页面)。通过此 VSC 扩展,只需单击一下即可直接从 VSC 启动基于 Web 的终端。 要打开 Web 终端,请单击: InterSystems Tools > 选择一个名称空间 > 单击以下图标之一 ( , )在 VSC 终端面板或浏览器上打开 Web 终端(按 Alt 更改默认图标): 对于至少具有 IRIS 2023.2 的用户 – 使用 WebSocket 终端 至少拥有 IRIS 2023.2 的用户可以利用最新版本的 VSC 扩展中包含的新“ WebSocket 终端”功能,并且不需要其他解决方法。 要打开 WebSocket 终端,请单击: InterSystems Tools > 选择一个名称空间 > 单击 Web 终端旁边的图标。 对于使用基于 Docker 的 IRIS 的用户 在 Docker 中使用 IRIS 环境并使用 VSC 的人员可以直接在 Docker 环境中启动终端会话。 单击状态栏中的 Docker 语音,然后选择Open Terminal in Docker 。 我要感谢@Evgeny.Shvarov 有关这一点的图片和解释。 对于在本地计算机上使用 2023.2 之前的 IRIS 版本的用户 对于使用在本地计算机上运行的 IRIS 版本的用户,可以在 VSC 中设置专用的 IRIS 终端: 打开设置.json 文件。您可以通过多种方式找到它,例如单击“视图”> “命令面板”> 输入:“设置”>打开用户设置 (JSON) 在“ terminal.integrated.profiles.windows ”下添加以下代码: "terminal.integrated.profiles.windows" :{ "IRIS Terminal" : { "path" : [ "C:\\InterSystems\\IRISHealth\\bin\\irissession.exe" ], "args" : [ "IRISHEALTH" ], "icon" : "terminal-cmd" } } 注意:插入irissession.exe的正确路径。 C。要从 VSC 打开终端,请导航至:终端>新终端>启动配置文件... > IRIS 终端。 d.终端菜单中现在应该可以使用“IRIS Terminal”语音: 对于使用 SSH 连接在基于远程服务器的 IRIS 上进行编码的用户 对于使用基于可通过 SSH 连接(例如使用PuTTY )访问的远程服务器(例如公司服务器)的 IRIS 版本的人员来说,可以使用远程 - SSH VSC扩展将 VSC 直接连接到服务器。为此: 安装远程 - SSH:编辑配置文件 VSC 扩展; 单击“远程资源管理器”图标 在侧边栏中; 选择“打开 SSH 配置文件” 并打开配置文件,路径为: C:\Users\<用户名>\.ssh\config 在配置文件中插入以下代码: Host my-putty-connection HostName < IP address or server name > User < username > IdentityFile < private key path on your local machine > Port < port > IP 地址和端口对应于 PuTTY 中指定的主机名和端口,用户名是用于访问远程服务器的用户凭据,IdentityFile 是 PuTTY 私钥的文件路径。 注意:VSC 无法读取 PuTTY (.ppk) 生成的私钥的原始格式。要通过 PuTTY 在 VSC 和远程服务器之间建立连接,您必须复制原始私钥并将新版本转换为 .pem 格式。为了进行转换: 启动 PuTTYgen 应用程序 在“文件”菜单下,单击“加载私钥” 选择 .ppk 格式的私钥,然后选择“打开” 在“转换”菜单下,单击“导出 OpenSSH 密钥”(强制使用新文件格式)。 设置扩展名为 .pem 的新名称,然后单击“保存”按钮。 将此新 .pem 文件的路径链接到 VSC 中的IdentifyFile 参数 保存文件。几秒钟后,新连接应出现在“远程资源管理器”面板中; 单击“在新窗口中连接... ”以在新的 VSC 窗口中打开 SSH 连接: 选择远程计算机的操作系统(仅在第一次访问时) 在新窗口中,导航至:终端>新终端(或使用快捷键 Ctrl + ò 或 Ctrl + Shift + ò)。 您现在已连接到远程计算机,并且可以在 VSC 中使用其 IRIS 终端。 注意:此操作仅在您之前通过 PuTTY 启动远程连接时有效,并且在 PuTTY 关闭或未连接到远程服务器时不起作用。此操作不会启动 PuTTY,它仅允许 VSC 连接到 PuTTY 建立的隧道。 要通过 VSC 启动 PuTTY 连接,您可以使用批处理文件(在 Windows 上)。提供的connect_remote.bat文件使用 PuTTY 附带的 Plink 命令来启动会话: @echo off set SESSION="<your saved session name>" plink -load %SESSION% 要启动会话,只需在 VSC 终端中键入.\connect_remote.bat以打开远程连接并插入您的凭据。 注意:后一种方法使您可以访问支持所有 VSC 快捷方式的终端版本!欢迎回来 Ctrl+V,再见 Shift+Insert 🎉
文章
YuHao Wan · 十一月 1, 2022

Caché实现SM3密码杂凑算法

### 0. 算法概述 SM3密码杂凑算法是中国国家密码管理局2010年公布的中国商用密码杂凑算法标准。该算法于2012年发布为密码行业标准(GM/T 0004-2012),2016年发布为国家密码杂凑算法标准(GB/T 32905-2016)。 SM3适用于商用密码应用中的数字签名和验证,是在[SHA-256]基础上改进实现的一种算法,其安全性和SHA-256相当。SM3和MD5的迭代过程类似,也采用Merkle-Damgard结构。消息分组长度为512位,摘要值长度为256位。 整个算法的执行过程可以概括成四个步骤:**消息填充、消息扩展、迭代压缩、输出结果**。 ### 1. 消息填充 SM3的消息扩展步骤是以512位的数据分组作为输入的。因此,我们需要在一开始就把数据长度填充至512位的倍数。具体步骤如下: 1、先填充一个“1”,后面加上k个“0”。其中k是满足(n+1+k) mod 512 = 448的最小正整数。 2、追加64位的数据长度。 ![消息填充](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-01.png) ### 2. 消息分组扩展 将填充后的消息m′按512比特进行分组:m′ = B(0)B(1)...B(n−1) 其中n=(l+k+65)/512。 SM3的迭代压缩步骤没有直接使用数据分组进行运算,而是使用这个步骤产生的132个消息字。(一个消息字的长度为32位/4个字节/8个16进制数字)概括来说,先将一个512位数据分组划分为16个消息字,并且作为生成的132个消息字的前16个。再用这16个消息字递推生成剩余的116个消息字。 ![消息分组](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-02.png) ### 3. 迭代压缩 SM3使用消息扩展得到的消息字进行迭代运算。 ![迭代运算](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-03.png) 初值IV被放在A、B、C、D、E、F、G、H八个32位变量中,整个算法中最核心、也最复杂的地方就在于**压缩函数**。压缩函数将这八个变量进行64轮相同的计算。 ![压缩函数](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-04.png) ### 4. 输出结果 将得到的A、B、C、D、E、F、G、H八个变量拼接输出,就是SM3算法的输出。 ![杂凑值输出](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-05.png) ### 5. 附录 ![符号](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-07.png) ![常数与函数](https://gitee.com/wanyuhao/intersystems/raw/master/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95SM3,SM4/images/SM3-06.png) ### 6. Caché实现 ``` /// SM3密码杂凑算法是中国国家密码管理局2010年公布的中国商用密码杂凑算法标准 /// 该算法于2012年发布为密码行业标准(GM/T 0004-2012),2016年发布为国家密码杂凑算法标准(GB/T 32905-2016)。 /// 对长度为l(l < 2^64)比特的消息m,SM3杂凑算法经过填充和迭代压缩,生成杂凑值,杂凑值长度为256比特。 Class Utility.SM3 Extends %RegisteredObject { /// Creator: wyh /// CreatDate: 2022-11-01 /// Description:SM3加密 /// Input: msg:原文 /// Output: 16进制密文 /// Debug: w ##class(Utility.SM3).Hashsm3("{""appId"":""60C90F3B796B41878B8D9C393E2B6329"",""nonceStr"":""1234567890"",""timestamp"":""60C90F3B796B41878B8D9C393E2B6329"",""version"":""V2.0.0""}F2D8D966CD3D47788449C19D5EF2081B") ClassMethod Hashsm3(msg) { #; 初始值IV =7380166f 4914b2b9 172442d7 da8a0600 a96f30bc 163138aa e38dee4d b0fb0e4e s V(0) = "01110011100000000001011001101111 01001001000101001011001010111001 00010111001001000100001011010111 11011010100010100000011000000000 10101001011011110011000010111100 00010110001100010011100010101010 11100011100011011110111001001101 10110000111110110000111001001110" #; 1. 消息填充 s m = ..s2m(msg) #; 2. 消息分组 #; 将填充后的消息m′按512比特进行分组:m′ = B(0)B(1)...B(n−1) #; 其中n=(l+k+65)/512。 s n = $l(m)/512 f i = 0 : 1 : n-1 d .s B(i) = $e(m, 512 * i + 1, 512 * (i + 1)) #; 3. 迭代压缩 #; 对m′按下列方式迭代: #; FOR i=0 TO n-1 #; V(i+1) = CF(V(i),B(i)) #; ENDFOR #; 其中CF是压缩函数,V(0)为256比特初始值IV,B(i)为填充后的消息分组,迭代压缩的结果为V(n)。 .s V(i+1) = ..CF(V(i) ,B(i)) #; 4. 杂凑输出 #; ABCDEFGH = V(n) #; 输出256比特的杂凑值y = ABCDEFGH。 s rtn = "" f i = 1 : 1 : 8 d .s bit = $p(V(n), " ", i) .s num = ..bs2n(bit) .s hex = $ZHEX(num) .i $l(hex) < 8 d ..f j = 1 : 1 : 8 - $l(hex) d ...s hex = "0" _ hex .s rtn = rtn _ hex s rtn = $zcvt(rtn, "L") return rtn } /// 1. 消息填充 /// 将填充后的消息m′按512比特进行分组:m′ = B(0)B(1)...B(n−1) /// 其中n=(l+k+65)/512 ClassMethod s2m(msg) { s len = $l(msg) s r = "" f i = 1 : 1 : len d .s num = $ascii($e(msg, i)) .s bs = ..n2bs(num) .s r = r _ bs s rtn = r s rtn = rtn _ "1" s k = 512 - (64 + ($l(r) + 1)) # 512 f i = 1 : 1 : k d .s rtn = rtn _ "0" s lenbs = ..n2bs($l(r)) s t = 64 - $l(lenbs) f i = 1 : 1 : t d .s rtn = rtn _ "0" s rtn = rtn _ lenbs return rtn } /// 3. 迭代压缩 /// 令A,B,C,D,E,F,G,H为字寄存器,SS1,SS2,TT1,TT2为中间变量,压缩函数V(i+1) = CF(V(i),B(i)), 0