搜索​​​​

清除过滤器
公告
Michael Lei · 二月 21, 2022

欢迎大家为中国参赛者Wu Fatian 踊跃投票!

投票三部曲: 1. 注册用户或登陆; 2. 成为社区活跃者获取投票资格 3. 投票 good! 欢迎大家把对我们的需求发表在讨论区: intersystems-产品学习使用问题经验分享讨论区--https://cn.community.intersystems.com/post/intersystems-%E4%BA%A7%E5%93%81%E5%AD%A6%E4%B9%A0%E4%BD%BF%E7%94%A8%E9%97%AE%E9%A2%98%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB%E8%AE%A8%E8%AE%BA%E5%8C%BA 这个绿色的 vote的 button 并不工作,试试这个链接 https://openexchange.intersystems.com/contest/21 it is a screenshot.... 点击查看 最新投票规则,在英文原帖的评论区我们的工作人员会每天更新排名,欢迎关注:D 暂时群众投票排名第一,@Fatian Wu加油! Expert Nomination, Top 3 appmsw-sql2xlsx by @Sergey Mikhailenko iris-python-dashboards by @Evgeniy Potapov DIMSE Iris Dicom Fhir Service by @Ron Sweeney ➡️ Voting is here. Community Nomination, Top 3 IRIS-Database-and-Machine-Learning-Based-Approaches-for-Prediction-of-Spontaneous-Intracerebral-Hemo by @Fatian Wu iris-python-dashboards by @Evgeniy Potapov appmsw-sql2xlsx by @Sergey Mikhailenko ➡️ Voting is here. Experts, we are waiting for your votes! 🔥 Participants, improve & promote your solutions! 武同学仍然排名第三,加油! Expert Nomination, Top 3 django-iris by @Dmitry Maslennikov appmsw-sql2xlsx by @Sergey Mikhailenko iris-python-dashboards by @evgeniy Potapov ➡️ Voting is here. Community Nomination, Top 3 django-iris by @Dmitry Maslenniko appmsw-sql2xlsx by @Sergey Mikhailenko IRIS-Database-and-Machine-Learning-Based-Approaches-for-Prediction-of-Spontaneous-Intracerebral-Hemo by @Fatian Wu
问题
jingwei lu · 三月 16, 2022

如何获得WRC网站登录权限

因为最近再研究Cache,发现Cache安装程序官方上找不到了,又从一个帖子上看到Cache安装程序在WRC上面有,话说这个WRC账号是不是类似于Oracle的MOS账号? 我从官网机器人那得到指示给support@intersystems.com发邮件,可能是因为塑料英语,石沉大海了。另外我想问问这个账号是不是只对那些买了产品或服务的客户单位公开? 请哪位大神为我指点迷津,谢谢 是的,WRC主要面向我们在服务有效期内的客户,请把您的单位名称写在邮件里,邮件也可以支持中文的,没有问题。谢谢! 既然是再研究,何不试试我们最新的InterSystems IRIS。如果以前研究过Caché 会发现,IRIS 还是同样的架构、同样的开发语言、同样的global存储,只是人机交互更流畅、性能更快,增加更多扩展,机器学习、python、丰富的外部BI工具适配、容器化部署、API管理等等等等,都等着你探索。 要想体验就在社区主页左边,选择下载InterSystems IRIS。 WRC可以说是我们对客户的支持中心,所以只面对我们的客户开放注册。
问题
Liang Jianhui · 八月 24, 2022

log文件内容存储到DB中

现在有一个需求。想将intersystems系统产生的alerts.log文件的内容保存到一个表中。有什么工具或者API 能自动格式化log文件到DB吗,还是需要手动解析log文件然后在存储到DB中。 这是两件事:1. 把log里的记录结构化。 2. 保存到DB。 关于1,如果你用的是最新的iris版本, 可以直接拿到结构化的log, 看这个链接: https://docs.intersystems.com/iris20221/csp/docbook/Doc.View.cls?KEY=ALOG 如果是老版本没有结构化,您得自己去做文本处理。好在这个log非常简单。比如下面两行: 08/30/22-11:03:57:775 (6964) 2 [Utility.Event] Previous system shutdown was abnormal, system forced down or crashed. Fast shutdown complete.08/30/22-11:03:59:541 (7716) 2 [Utility.Event] Preserving journal files c:\intersystems\hcdemo\mgr\journal\20220824.002 and later for journal recovery and transaction rollback如果您要只是把日期时间,警告类型,级别,内容分开,它基本就是分开的,用的是空格。 如果您想把内容页结构化,这就花功夫了, 我上面的链接里的工具也不做这个事。 2. 存入DB您得自己解决,IRIS提供文本,API, 没有SQL查询,好在有一大堆监控工具都干这事,就是监控文本文件写入数据库。
文章
姚 鑫 · 二月 4, 2022

第四十四章 SQL函数 DATENAME

# 第四十四章 SQL函数 DATENAME 日期/时间函数,它返回一个字符串,表示日期/时间表达式中指定部分的值。 # 参数 - `datepart` - 要返回的日期/时间信息类型。 日期或时间部分的名称(或缩写)。 可以用大写或小写指定该名称,也可以不加引号。 可以将`datepart`指定为文字或主机变量。 - `date-expression` - 要返回`datepart`值的日期、时间或时间戳表达式。 日期表达式必须包含`datepart`类型的值。 # 描述 `DATENAME`函数返回日期/时间值中指定部分的名称(例如`“June”`)。 结果作为数据类型`VARCHAR(20)`返回。 如果结果是数字(例如`“23”`表示当天),它仍然作为`VARCHAR(20)`字符串返回。 要以整数形式返回此信息,请使用`DATEPART`。 要返回包含多个日期部分的字符串,请使用`TO_DATE`。 请注意,`DATENAME`是为Sybase和Microsoft SQL Server兼容性而提供的。 这个函数也可以通过调用`DATENAME()`方法从ObjectScript调用: ``` $SYSTEM.SQL.Functions.DATENAME(datepart,date-expression) ``` # Datepart 参数 `datepart`参数可以是包含一个(且仅包含一个)以下日期/时间组件的字符串,可以是全名(`date Part`列),也可以是缩写(缩写列)。 这些`datepart`组件名称和缩写不区分大小写。 Date Part |Abbreviations| Return Values ---|---|--- year| yyyy, yy| 0001-9999 quarter| qq, q| 1-4 month| mm |January,...December week |wk, ww| 1-53 weekday |dw |Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday dayofyear| dy, y| 1-366 day| dd, d| 1-31 hour| hh| 0-23 minute |mi, n| 0-59 second |ss, s| 0-59 millisecond |ms| 0-999 (with precision of 3) microsecond |mcs| 0–999999 (with precision of 6) nanosecond| ns |0–999999999 (with precision of 9) 如果将无效的`datepart`值指定为文字,则会发出`SQLCODE -8`错误码。 但是,如果提供一个无效的`datepart`值作为主机变量,则不会发出`SQLCODE`错误,并且`DATENAME`函数返回一个`NULL`值。 上表显示了不同日期部分的默认返回值。 通过使用带有不同时间和日期选项的`SET OPTION`命令,可以修改其中几个日期部分的返回值。 `week`:可以配置为使默认算法或ISO 8601标准算法确定给定日期的年度星期。 `weekday`:对于`weekday`的默认设置是将周日指定为一周的第一天(工作日=1)。 但是,可以将一周的第一天配置为另一个值,或者可以应用指定星期一为一周的第一天的ISO 8601标准。 `millisecond`:返回一个包含毫秒数(千分之一秒)的字符串。 如果日期表达式的精度超过`3`个小数位数,将其截断为`3`个数字,并将该数字作为字符串返回。 如果日期表达式具有指定的精度,但精度小于`3`个小数位数,则 `0`将其填充为`3`个数字,并将该数字作为字符串返回。 微秒和纳秒执行类似的截断和填充零。 可以将`datepart`指定为带引号的字符串或不带引号的字符串。 这些语法变体执行的操作略有不同: - 引号:`DATENAME('month','2018-02-25')`:在创建缓存查询时,`datepart`被视为一个字面值。 SQL执行文字替换。 这将产生一个更普遍的可重用的缓存查询。 - 没有引号:`DATENAME(month,'2018-02-25')`:在创建缓存查询时,`datepart`被视为关键字。 没有文字替换。 这将产生一个更具体的缓存查询。 # 日期表达格式 `date-expression`参数可以是以下任何一种格式: - `%Date` logical value (`+$H`) - `%PosixTime` (`%Library.PosixTime`) logical value (an encoded 64-bit signed integer) - `%TimeStamp` (`%Library.TimeStamp`) logical value (`YYYY-MM-DD HH:MM:SS.FFF`), also known as ODBC format. - `%String` (or compatible) value `%String`(或compatible)值可以是以下任何格式: - 99999,99999 ($H format) - Sybase/SQL-Server-date Sybase/SQL-Server-time - Sybase/SQL-Server-time Sybase/SQL-Server-date - Sybase/SQL-Server-date (default time is 00:00:00) - Sybase/SQL-Server-time (default date is 01/01/1900) Sybase/SQL-Server-date是以下五种格式之一: ```sql mmdelimiterdddelimiter[yy]yy dd Mmm[mm][,][yy]yy dd [yy]yy Mmm[mm] yyyy Mmm[mm] dd yyyy [dd] Mmm[mm] ``` 其中分隔符是斜杠(`/`)、连字符(`-`)或句号(`.`)。 Sybase/SQL-Server-time表示以下三种格式之一: ```sql HH:MM[:SS:SSS][{AM|PM}] HH:MM[:SS.S] HH['']{AM|PM} ``` 如果`date-expression`指定了时间格式但没有指定日期格式,则`DATENAME`的默认值为`1900-01-01`,其中`weekday`的值为`Monday`。 # 范围和值检查 `DATENAME`对输入值执行以下检查。 如果一个值检查失败,则返回`null`字符串。 - 有效的日期表达式可以由日期字符串(`yyyy-mm-dd`)、时间字符串(`hh:mm:ss`)或日期和时间字符串(`yyyy-mm-dd hh:mm:ss`)组成。 如果同时指定日期和时间,则日期和时间都必须有效。 例如,如果没有指定时间字符串,则可以返回`Year`值,但如果指定了无效的时间字符串,则无法返回Year值。 - 日期字符串必须完整,格式正确,包含适当数量的元素和每个元素的数字,以及适当的分隔符。 例如,如果省略了`Day`值,则不能返回`Year`值。 年必须指定为四位数字。 - 时间字符串必须使用适当的分隔符进行适当的格式化。 因为时间值可以为零,所以可以省略一个或多个时间元素(保留或省略分隔符),这些元素将返回值为零。 因此,`“hh: mm: ss”,“hh: mm:”`、`“hh: mm”,“hh:: ss”,“hh::”`、`“hh”`,和`“::”`都是有效的。 若要省略`Hour`元素,`date-expression`必须没有字符串的日期部分,并且必须保留至少一个分隔符(`:`)。 - 日期和时间值必须在有效范围内。 年龄:0001到9999。 月份:1 - 12个月。 天数:1 - 31天。 小时:0到23。 分钟:0到59分钟。 秒:0 ~ 59。 - 一个月中的天数必须与月和年相匹配。 例如,日期`“02-29”`仅在指定的年份为闰年时有效。 - 大多数小于10的日期和时间值可能包括或省略前导零。 但是,小于10的`Hour`值必须包括前导0,如果它是`datetime`字符串的一部分。 不允许使用其他非规范整数值。 因此,`Day`值为`“07”`或`“7”`是有效的,但`“007”`、`“7.0”`或`“7a”`无效。 - 如果`date-expression`指定了时间格式但没有指定日期格式,则`DATENAME`不会对时间组件值执行范围验证。 # 示例 在下面的例子中,每个`DATENAME`返回`'Wednesday'`,因为它是指定日期的星期几(`'dw'`): ```sql SELECT DATENAME('dw','2018-02-21') AS DayName, DATENAME(dw,'02/21/2018') AS DayName, DATENAME('DW',64700) AS DayName Wednesday Wednesday Wednesday ``` 下面的例子返回'`December'`,因为它是指定日期的月份名称(`'mm'`): ```sql SELECT DATENAME('mm','2018-12-20 12:00:00') AS MonthName December ``` 下面的示例返回`'2018'`(字符串形式),因为它是指定日期的年份(`'yy'`): ```sql SELECT DATENAME('yy','2018-12-20 12:00:00') AS Year 2018 ``` 注意,上面的例子使用了日期部分的缩写。 但是,你可以指定全名,如下例所示: ```sql SELECT DATENAME('Q',$HOROLOG) AS Q, DATENAME('WK',$HOROLOG) AS WkCnt, DATENAME('DY',$HOROLOG) AS DayCnt 1 6 35 ``` 下面的嵌入式SQL示例将`datepart`和`date-expression`作为宿主变量传入: ```java ClassMethod DateName() { s a="year" s b=$HOROLOG &sql(SELECT DATENAME(:a,:b) INTO :c) w "this year is: ",c } ``` ``` DHC-APP>d ##class(PHA.TEST.SQLCommand).DateName() this year is: 2022 ``` 下面的示例使用子查询从出生日期为星期三的`Sample.Person`返回记录: ```java SELECT Name AS WednesdaysChild,DOB FROM (SELECT Name,DOB,DATENAME('dw',DOB) AS Wkday FROM Sample.Person) WHERE Wkday='Wednesday' ORDER BY DOB ```
问题
Botai Zhang · 四月 13, 2021

编码格式转换

在使用过程中,碰到对接一些国内编码例如:GBK2312等系列的编码格式,碰到这类情况,应该如何处理?各位有没有好的解决方式?期待解答交流! 如果需要在InterSystems IRIS中对不同的编码的数据进行转换,可以使用函数$ZCVT。 例如将变量tStr的unicode值转换为GB2312: s tOutput=$ZCVT(tStr,"O","GB2312") 感谢回复!这种方式尝试过,但是,$ZCVT并不支持GB2312。 用GB18030,它向后兼容GB2312
公告
Michael Lei · 六月 22, 2022

CloudStudio - 一个纯浏览器的代码编辑器

Hi 社区, 这是海外工程师做的一个纯浏览器的代码编辑器CloudStudio. 欢迎大家下载试用: GitHub 下载: https://github.com/SeanConnelly/CloudStudio InterSystems 应用市场下载:https://openexchange.intersystems.com/package/CloudStudio 到Discord 讨论区:https://discord.gg/ZnvdMywsjP Docker 支持与在线Demo: 要求 已安装 git 和 Docker desktop . 安装 Clone/git 把 repo 导入任何本地目录 git https://github.com/rcemper/Dataset-OEX-reviews.git 启动IRIS容器: docker-compose up -d --build 如何测试 http://localhost:42773/cloudstudio/CloudStudio.Index.cls 或使用在线Demo Demo 视频:https://www.youtube.com/watch?v=Am6QAvrPPPg
公告
Claire Zheng · 十一月 22, 2021

参与Gartner Peer Insights同业评审,赢取价值$25的礼品卡

亲爱的社区开发者们,大家好! 现在参与Gartner Peer Insight同业评审对我们的产品做出评价,可获得价值 $25 美元的礼品卡。评论观点需中立客观(InterSystems员工不允许参加)并被Gartner审核通过。点击此处开始: https://gtnr.it/3ulVX4K 。 对流程不熟悉的同学,可以参考一下我们此前发布的一篇旧贴。 调研仅花费您很少时间,欢迎大家积极参与! 活动已经截止了。。。
文章
姚 鑫 · 九月 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` 用户帐户下运行。
文章
姚 鑫 · 五月 9, 2021

第四章 多维存储的SQL和对象使用(一)

# 第四章 多维存储的SQL和对象使用(一) 本章介绍InterSystems IRIS®对象和SQL引擎如何利用多维存储(全局变量)来存储持久对象、关系表和索引。 尽管InterSystems IRIS对象和SQL引擎会自动提供和管理数据存储结构,但了解其工作原理的详细信息还是很有用的。 数据的对象视图和关系视图使用的存储结构是相同的。为简单起见,本章仅从对象角度介绍存储。 # 数据 每个使用`%Storage.Persistent`存储类(默认)的持久化类都可以使用多维存储(全局变量)的一个或多个节点在InterSystems IRIS数据库中存储其自身的实例。 每个持久化类都有一个存储定义,用于定义其属性如何存储在全局变量节点中。这个存储定义(称为“默认结构”)由类编译器自动管理。 ## 默认结构 用于存储持久对象的默认结构非常简单: - 数据存储在名称以完整类名(包括包名)开头的全局变量中。附加`“D”`以形成全局数据的名称,而附加`“I”`作为全局索引。 - 每个实例的数据都存储在全局数据的单个节点中,所有非瞬态属性都放在`$list`结构中。 - 数据全局变量中的每个节点都以对象`ID`值作为下标。默认情况下,对象`ID`值是通过调用存储在全局变量数据根(没有下标)的计数器节点上的`$Increment`函数提供的整数。 例如,假设我们定义了一个简单的持久化类`MyApp.Person`,它有两个文本属性: ```java Class MyApp.Person Extends %Persistent { Property Name As %String; Property Age As %Integer; } ``` 如果我们创建并保存此类的两个实例,得到的全局变量结果将类似于: ```java ^MyApp.PersonD = 2 // counter node ^MyApp.PersonD(1) = $LB("",530,"Abraham") ^MyApp.PersonD(2) = $LB("",680,"Philip") ``` **注意,存储在每个节点中的`$List`结构的第一部分是空的; 这是为类名保留的。 如果定义`Person`类的子类,则此槽包含子类名。 当多个对象存储在同一个区段内时,`%OpenId`方法(由`%Persistent`类提供)使用此信息多态地打开正确的对象类型。 此槽在类存储定义中显示为名为`“%%CLASSNAME”`的属性。** ## IDKEY `IDKEY`机制允许显式定义用作对象`ID`的值。为此,只需将`IDKEY`索引定义添加到类中,并指定将提供`ID`值的一个或多个属性。请注意,一旦保存对象,其对象`ID`值就不能更改。这意味着在保存使用`IDKEY`机制的对象后,不能再修改该对象`ID`所基于的任何特性。 ```java Class MyApp.Person Extends %Persistent { Index IDKEY On Name [ Idkey ]; Property Name As %String; Property Age As %Integer; } ``` 如果我们创建并保存`Person`类的两个实例,得到的全局变量结果现在类似于: ```java ^MyApp.PersonD("Abraham") = $LB("",530,"Abraham") ^MyApp.PersonD("Philip") = $LB("",680,"Philip") ``` 请注意,不再定义任何计数器节点。还要注意,通过将对象`ID`基于`Name`属性,我们已经暗示了`Name`的值对于每个对象必须是唯一的。 如果`IDKEY`索引基于多个属性,则主数据节点具有多个下标。例如: ```java Class MyApp.Person Extends %Persistent { Index IDKEY On (Name,Age) [ Idkey ]; Property Name As %String; Property Age As %Integer; } ``` 在这种情况下,生成的全局变量现在类似于: ```java ^MyApp.PersonD("Abraham",530) = $LB("",530,"Abraham") ^MyApp.PersonD("Philip",680) = $LB("",680,"Philip") ``` **重要提示:`IDKEY`索引使用的任何属性的值中都不能有连续的一对竖线(`||`),除非该属性是对持久类实例的有效引用。 这种限制是由InterSystems SQL机制的工作方式强加的。 在`IDKey`属性中使用`||`会导致不可预知的行为。** ## Subclasses 默认情况下,持久性对象的子类引入的任何字段都存储在附加节点中。 子类的名称用作附加的下标值。 例如,假设我们定义了一个具有两个文本属性的简单持久`MyApp.Person`类: ```java Class MyApp.Person Extends %Persistent { Property Name As %String; Property Age As %Integer; } ``` 现在,我们定义了一个持久子类`MyApp.Students`,它引入了两个额外的文本属性: ```java Class MyApp.Student Extends Person { Property Major As %String; Property GPA As %Double; } ``` 如果我们创建并保存此`MyApp.Student`类的两个实例,得到的全局结果将类似于: ```java ^MyApp.PersonD = 2 // counter node ^MyApp.PersonD(1) = $LB("Student",19,"Jack") ^MyApp.PersonD(1,"Student") = $LB(3.2,"Physics") ^MyApp.PersonD(2) = $LB("Student",20,"Jill") ^MyApp.PersonD(2,"Student") = $LB(3.8,"Chemistry") ``` 从`Person`类继承的属性存储在主节点中,而由`Student`类引入的属性存储在另一个子节点中。这种结构确保了学生数据可以作为人员数据互换使用。例如,列出所有`Person`对象名称的SQL查询正确地获取`Person`和`Student`数据。当属性被添加到超类或子类时,这种结构还使类编译器更容易维护数据兼容性。 请注意,主节点的第一部分包含字符串`“Student”`-它标识包含学生数据的节点。 ## 父子关系 在父子关系中,子对象的实例存储为它们所属的父对象的子节点。这种结构确保子实例数据与父数据在物理上是集群的。 ```java /// An Invoice class Class MyApp.Invoice Extends %Persistent { Property CustomerName As %String; /// an Invoice has CHILDREN that are LineItems Relationship Items As LineItem [inverse = TheInvoice, cardinality = CHILDREN]; } ``` 和`LineItem`: ```java /// A LineItem class Class MyApp.LineItem Extends %Persistent { Property Product As %String; Property Quantity As %Integer; /// a LineItem has a PARENT that is an Invoice Relationship TheInvoice As Invoice [inverse = Items, cardinality = PARENT]; } ``` 如果我们存储多个`Invoice`对象的实例,每个实例都有关联的`LineItem`对象,则得到的全局变量结果将类似于: ```java ^MyApp.InvoiceD = 2 // invoice counter node ^MyApp.InvoiceD(1) = $LB("","Wiley Coyote") ^MyApp.InvoiceD(1,"Items",1) = $LB("","Rocket Roller Skates",2) ^MyApp.InvoiceD(1,"Items",2) = $LB("","Acme Magnet",1) ^MyApp.InvoiceD(2) = $LB("","Road Runner") ^MyApp.InvoiceD(2,"Items",1) = $LB("","Birdseed",30) ``` ## 嵌入对象 存储嵌入对象的方法是先将它们转换为序列化状态(默认情况下是包含对象属性的`$List`结构),然后以与任何其他属性相同的方式存储此串行状态。 例如,假设我们定义了一个具有两个文字属性的简单串行(可嵌入)类: ```java Class MyApp.MyAddress Extends %SerialObject { Property City As %String; Property State As %String; } ``` 现在,我们修改前面的示例以添加嵌入的`Home Address`属性: ```java Class MyApp.MyClass Extends %Persistent { Property Name As %String; Property Age As %Integer; Property Home As MyAddress; } ``` 如果我们创建并保存此类的两个实例,则生成的全局变量相当于: ```java ^MyApp.MyClassD = 2 // counter node ^MyApp.MyClassD(1) = $LB(530,"Abraham",$LB("UR","Mesopotamia")) ^MyApp.MyClassD(2) = $LB(680,"Philip",$LB("Bethsaida","Israel")) ``` ## 流 **通过将全局流的数据拆分成一系列块(每个块小于`32K`字节)并将这些块写入一系列顺序节点,全局流被存储在全局流中。文件流存储在外部文件中。**
文章
Qiao Peng · 一月 31, 2024

用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 · 一月 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 · 十二月 2, 2022

通过智能数据编织应对数据挑战

1.数据的价值 数据的核心价值是帮助我们决策。 我们无时无刻不在决策,大到战略决策——为一家新医院选址,还有战术决策——鉴别产品的目标市场或抵押贷款审批,更频繁的是操作决策——决定患者的手术方案或患者药物的调整。 这些决策要求不同的决策速度,传统的数据中心已经能较好地帮助我们做战略决策、战术决策,甚至一些操作决策。但新的业务需求要求我们的决策速度越来越快,甚至借助机器学习自动为我们做出即时的决策,例如批准还是拒绝一笔信用卡交易或基于算法自动交易。 无论是人工决策还是基于机器学习的自动决策,决策的依据是数据。数据的速度和质量决定了决策的速度和质量。要支持决策,需要数据具有如下特征: (1)完整 :关联且具有完整上下文; (2)干净 :数据质量没有问题; (3)及时 :在决策点上没有延迟。 传统数据中心很难在及时性上满足要求。 2.数据挑战 数字化浪潮下,我们面临更大的数据挑战: 数据规模:数据量已经完全超出了人工处理能力。 数据源多样性:数据源不再仅是数据库,流式引擎的消息、物联网、对象存储......它们还带来了越来越多模型种类的数据。 更多的数据孤岛:更多的系统和应用被建设,进一步增加了数据孤岛现象。 跨部门的数据不一致:统计口径和统计时间的差异,造成财务部门统计的数据,总是和业务部门统计的数据对不上。 数据服务对象变化:现在业务分析师、运营数据消费者、数据工程师、数据科学家和普通人群都是服务对象。 部署需求的多样性:传统本地部署、云部署、混合部署...... 而由于技术、法律、经济性等原因,传统的数据集中保存无以为继...... 数据库、数据仓库、数据湖,这些传统的数据管理技术应对这些需求和挑战,越来越力不从心。数据库能保持数据的热度(良好的数据存取速度),但支持的模型类型和数据来源有限;数据仓库要统一数据质量与格式(Schema on Write),缺乏灵活性;数据湖可以“以原始形态保存一切数据” (Schema on Read),但各种数据进入这样一个湖,全都变成了无法直接分析利用的冷数据! 应对之道 – 数据编织和智能数据编织 数据编织是正在兴起的数据管理技术以应对数据挑战,Gartner将其定为2022年12大战略技术趋势之首。 那什么是数据编织? Gartner将数据编织定义为一种设计概念,可作为数据和连接流程的集成层(结构)。通过对现有、可发现和可推理的元数据资产进行持续分析,数据编织能够在所有环境(包括混合云和多云平台)中设计、部署和利用可重复使用的集成数据。 智能数据编织(Smart Data Fabric)则更进一步,在结构中直接嵌入各种分析能力,包括数据探索、商业智能、自然语言处理和机器学习,使企业可以更快、更容易地获得全新洞察,为智能预测和规范性服务及应用提供动力。 Gartner的这个名词解释还是有些抽象,如何理解它?为何数据编织/智能数据编织是解决上述挑战的利器? 如何利用现有的产品真实实现智能数据编织的落地? InterSystems提供的智能数据编织解决方案 今天,智能数据编织(Smart Data Fabric)正被用于许多行业的实际生产中,为各种企业级、关键任务创新提供动力,包括场景规划和决策支持、法规遵从、实时可见性和警报等,作为全球领先的数据技术提供商,为应对当前数据挑战,特别通过InterSystems IRIS新一代数据平台提供智能数据编织解决方案,整合了许多关键特性和能力,以满足客户实施智能数据编织进行数字化转型的需求,该方案在解决数据挑战的同时,允许现有的遗留应用和数据保持原位,最大限度地利用以前的技术投资,包括现有的数据湖和数据仓库,而不需要“撕裂和替换”任何现有技术。 InterSystems IRIS数据编织解决方案把智能数据编织分为3个阶段: 数据互联互通阶段:有能力实时、双向打通各种数据源,将数据源有机编织在一起。 数据集成阶段:对数据本身进行编织,为多模型数据提供高性能存取和转换、加入数据安全控制、建立数据谱系、抽象为干净和统一的语义层供数据用户使用。 智能利用阶段:对建立了统一语义的数据提供紧贴数据的智能利用能力,例如商业智能分析、自然语言分析、机器学习,并使这些智能增强数据编织本身。 InterSystems IRIS数据平台在单一技术栈内提供智能数据编织的这些能力:互联互通、数据集成、自助服务、智能分析和多云 。 传统数据利用的是多级瀑布模式:数据从数据库到数据湖,再到数据中心,数据大批量、高延时地在异构数据平台间移动和拷贝。这是影响数据时效性、一致性的主要原因。所以InterSystems智能数据编织第一就要解决这个问题,而解决之道就是互联互通和数据集成。 1.互联互通 因为数据源和数据模型的多样性,传统的ETL在能力和时效性上都已不能满足需求,需要更完整的互联互通能力。长期以来,InterSystems是互操作技术的领导者,它提供各种适配器实时接入各种数据源,例如流式处理引擎Kafka,并对遗留系统进行现代化,即便有很多遗留系统作为数据源,依然可以通过它将其数据模型多态化和数据服务现代化。 2.数据集成 数据集成(Integration)不追求将数据放在一起,而是要建立数据间的准确关联,建立具有连续上下文的全息数据,甚至丰富数据。InterSystems提供: (1)多模型 面对多元数据,Gartner表示,要想成功利用数据编织,企业必须确保数据编织能够动态地(通过元数据驱动设计)支持不同数据交付风格的组合,以支持特定的用例。 InterSystems的多模型数据建模和保存能力,让不同的数据以最适合的模型进行操作,它支持原生的对象、表、键值对和JSON文档。 (2)多语言 如何操作多模型的数据?每个数据用户都有熟悉或适合其用途的语言来使用数据,例如很多场景下,SQL是最简单的使用数据的语言。InterSystems让用户可以用SQL操作一切数据,哪怕它是以键值对建模和保存的。 (3)数据转换 不同的数据用户希望得到不同结构的数据。InterSystems提供图形化的高效数据转换工具,为用户构建干净的、单一可信的数据。 3.自助服务 如何发现、探索、推理数据编织平台中的数据?需要借助统一的语义和自助的服务能力。 (1)统一语义 为了数据完整性,无论是数据仓库还是数据湖,都将数据中心化存储。这造成了很多数据障碍:数据的时效性低、数据的质量参差不齐、数据需求严重依赖IT去清洗关联等等。 InterSystems的自适应分析是一个统一的、抽象的语义层,通过建立虚拟/逻辑数据分析立方体,用户可以使用SQL或BI工具访问这个语义层,而自适应分析自动使用SQL访问后台的多InterSystems IRIS数据平台实例获得数据和分析结果,不需要将数据集中保存到一起。 数据无需集中,因此无需ETL,没有数据抽取拷贝的时间成本开销,提供高时效性的数据;而抽象语义层将多数据源的数据建立逻辑关联,向用户提供干净、完整的语义上下文。 (2)行业语义级的数据编织 医疗健康行业面临相较其它行业更复杂的行业数据,在现实业务中要应对不同的语义表达。编织不同语义的数据源,将数据抽象为非标准语义,这会为后面的数据价值利用增加障碍。 FHIR建立统一行业语义的行业数据内容标准、利用标准行业术语和标识符、定义统一的传输标准、并逐步建立隐私和安全标准,让使用行业语义编织数据成为可能。 InterSystems支持所有FHIR的交互范式,提供FHIR服务器和FHIR资源仓库,并通过FHIR SQL构建器,建立基于SQL的FHIR数据访问能力,用最简单的数据操作支持BI/AI。通过FHIR来搭建具有统一行业语义和生态的数据编织平台。 (3)自助服务 长久以来,由于数据源和数据本身的复杂性,专业IT用户把持着数据的使用,商业用户极度依赖于专业用户才能获取、分析和利用数据。 借助统一语义层和对标准的支持,InterSystems让商业用户使用自己熟悉的工具和语言,例如SQL、BI工具和API来探索数据、操作数据和分析数据。 4.智能分析 为数据编织增加智能, InterSystems提供开放的智能分析能力。包括嵌入平台的机器学习、自然语言分析、商业智能特性,对第三方工具和生态的支持,以及对标准的支持——MDX、UIMA、PMML...... InterSystems提供全SQL操作方式的自动化机器学习,并允许使用第三方的自动化机器学习后台,如DataRobot,从而避免学习不同的API,用最简单的SQL就可以获得丰富的机器学习能力: 5.多云 数据编织平台要能支持所有主要的开发和部署环境,使开发人员和运营团队能够在他们选择的环境中工作。并与现有的基础设施和最佳技术无缝集成,支持最广泛的客户环境和应用要求。 公共云、私有云、本地、混合、裸机和虚拟机环境,InterSystems支持所有部署选项,且都只需要一个API,而不需要对你的应用程序进行修改。 6.单一技术栈而不建议不同产品和技术的堆砌与粘合 这一点至关重要。从整个数字化、信息化行业来看,信息部门尤其是最终用户的人员配置是远远不足的,从信息化部/科技部到我们的服务厂商,大部分从业人员工作负载基本都处在一个饱和甚至过饱和的状态,同时,IT从业人员由于薪酬激励的原因,大部分的中高端IT人才集中在少数行业或者少数头部厂商。针对这种情况,在数智底座的技术选择上,应该采取“少就是多”的策略,选择尽可能少的技术和产品种类,或者说一体化的技术架构,用一套技术体系来支持多种业务应用的实现,从而降低管理和学习成本。 另外一个原因,比如传统闭源厂商,很多软件产品都是收购或者是不同团队部门研发的,不同软件之间也需要集成或者胶水(参考文章:统一技术的高效数据架构能帮助客户节约大量成本)成本也是极其昂贵的。如果是选择开源技术栈,除了集成以外,还要考虑更多的比如技术路线的生命周期持久度,比如今天选择的技术会不会很快就过时了,技术支持如何延续?此外开源技术人员的成本普遍高于传统软件,人员流动也比较强,这也是企业选型必须考虑的问题。 总结 InterSystems的智能数据编织解决方案通过对数据源、数据的编织,避免多级瀑布式的大规模、高延迟的数据拷贝。构建抽象的统一语义层,并借助行业标准语义,建立基于标准的数据编织平台,为用户提供简单易用的数据探索和利用能力。利用全面的智能分析能力提升数据质量和数据价值,并降低数据利用的难度。多云的架构确保了对最广泛的客户环境和应用要求的支持,为数智底座的实施部署铺平了道路。
文章
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
文章
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 🎉 @Hao.Ma Thank you for translating my article. I hope you liked it 🙂