PowerBuilder编程新思维10.5:外传2(PowerPlume下一代开发解决方案)

发布时间 2023-10-05 11:02:07作者: windfic

万里归来年愈少

 

PowerBuilder编程新思维10.5:外传2(PowerPlume下一代解决方案)

 

前言

今天我们就来盘点一下,PB下一代开发的所有技术可能性。所谓下一代开发技术,就是指脱离或半脱离PBVM的应用开发技术,主要指后端。

 

后端技术汇总

 

  前端PB+JSON 前端PB+BLOB WEB
后端PBVM PbNode/Pbgo PbniServ,PowerServer PbNode
后端无代码 GraphQL SatRDA/VDN
PB转浏览器 PbXds VDN PbXds
PB转C# PowerServer PowerServer PowerServer
其它后端方案 PbPdd EXTPB.NET SatWeb

 

SatRDA

SatRDA是远程数据访问组件,使用它无需要改动原来的数据访问代码就可以支持外网访问远程数据库。

评价:商用方案(由「成都Only」提供)。

SatWeb

SatWeb是远程数据访问组件的Web版,使用它可以远程访问DW(纯Web方案H5DW)。

评价:商用方案(由「成都Only」提供)。这是最接近PowerPlume的方案,包括SatReport都非常好。

VDN

通过Web组件实现PB程序的浏览器运行,不用修改主程序代码,瞬间实现类似B/S的效果。

评价:商用方案(由「烟台沃森」提供)。

EXTPB.NET

基于.Net开发,采用PBNI和ActiveX技术实现PB程序的浏览器运行,充分利用了DataWindow控件的功能。

评价:商用方案(由「广州同享」提供)。黄总在群里说会在H5上发力,期待有突破性进展。

PBNIServ

It uses the PBNI to execute functions within a non-visual userobject. 

评价:商用方案(由「Topwiz」提供)。

PowerServer

官方的方案。

评价:商用方案(由「appeon」提供)。

GraphQL

利用GraphQL框架搭建后端。

评价:开源方案。不成熟,PB程序转换跨度大,可行性

PbGo

是一套PB开发Web服务的后端框架(基于golang的PBNI扩展),可以做为静态站点服务器,支持 RESTful 风格API。

评价:开源/商用方案(由「杭州-夕木双」提供)。

PbNode

Node.js环境插件,可以调用PB代码,返回Web或JSON。

最初的尝试,源码放在群里面了。单纯地在服务器运行一些PB代码,已经有相当多的产品支持了。再做一个没有什么质的提升。

评价:开源方案(由「PowerPlume」提供)。性能堪忧,PB程序转换成本高,可行性

PbPdd

利用多线程访问PBVM,返回JSON,可以做三层服务器,算是前端多线程的后端应用。

评价:商用方案(由「PowerPlume」提供)。PB转换成本较小,性能受限,可行性

PbXds

PB下一代开发解决方案(neXt-generation Development Solution),分成四个部分

  • PbXtc 下一代Trans Compiler编译器(golang/js)
  • PbXdw 下一代DataWindow(js)
  • PbXrt 下一代虚拟机接口运行时(c/js)
  • PbXsf 下一代Service运行框架(golang)

利用PbXtc编译器,将PB程序整体转换为js前端应用。程序也可以在后端(golang)运行,甚至还能在手机上运行。

评价:商用方案(由「PowerPlume」提供)。PB转换成本较小,但功能复杂,可行性

 

技术分析

 

PbXdw 分析

PbXdw是前端的核心代码,成都Only的SatWeb已经有成品发布了,同我的思路还是有区别的

PbXdw的目标是能像素级兼容,在打印时无需调整。要严丝合缝,这个工作量比较多。

 

PbXsf 分析

PbXsf 是后端的核心代码,就是不调用PBVM的情况下,支持数据的增删改查。静态解析PBL,生成可运行的后端代码。首先它要解析PBL,继而解析DW,再连接数据库,最后启动Web服务。有了PbXsf ,我们就可以把DW变成三层架构,可以在互联网访问。

 

PbXrt 分析

PbXrt是前后端共用的PBVM功能API接口,分成前端和后端两个部分。前端用JS语言,后端用C语言。

 

PbXtc 分析

PbXts,将Pb程序编译为Js。

 

在网上看过关于正阳的传闻,他们可能是第一个做非官方编译器的开发者,可惜还是失败了。说起来,也是生不逢时,在06年搞纯JS前端实在太困难了。现在,在无数前端开源项目的铺垫下,难度降低太多。有了前车之鉴,成功就有可能了。

Q: APB是一个什么产品?
A: APB 是 Appeon for PowerBuilder 的缩写。
Appeon 公司专注于实现 C/S 架构的 PowerBuilder 应用迁移到 B/S 架构的完整解决方案。Appeon for PowerBuilder 是这个解决方案的核心,是一套平台型软件产品。

Q: PB转换后的程序是Java代码或JSP吗?
A: 转换后的文件格式包括: HTML 、 XML 、 JavaScript 、 Image 文件。不是 Java 代码,也不是 JSP 。

Q: 用APB转换后的Web应用和JSP开发的Web应用,技术上有什么区别?
A: APB 在客户端使用的是 ActiveX 技术,服务器端使用的是 J2EE 技术,包括 Servlet 、 EJB 、 JDBC 。 APB 综合使用了 ActiveX 技术和 J2EE 技术。
JSP 则主要是服务器端的技术,给客户端传来的是 HTML 标记的文档,用于表现用户界面。对于一些要求复杂用户交互的应用,用 JSP 实现起来很困难。

Q: 前后台数据是XML传递的吗,可否在其它JSP程序中使用这些XML数据?
A: 前后台之间数据传递是二进制格式。前后台的调用和数据传输都是基于 http 的 RPC 。调用格式和传输的数据都是二进制的。这样做主要是出于安全和性能的考虑。
APB 早期的技术里,曾采用过 XML 来传输数据,但由于 XML 的解析和装配需要一定的时间,会影响运行的速度。

Q: 现有控件是用Javascript实现的吗?
A: PB 中的控件,如 DataWindow 、 Treeview 、 Tab 等都是用 ATL 技术实现的, Javascript 难以实现这么复杂的控件。还有如 Blob 的类型, JavaScript 也难以实现。

Q: 为什么要采用IE插件的技术?不要插件行吗?
A: 使用 ActiveX 插件是为了最大程度上,支持 PowerBuilder 的各种控件、对象和功能特性。例如 Datawindow 控件、 TreeView 控件、 Dragdrop 等。这些功能在一般的 Web 应用中很难实现。
APB 在迁移 Web 应用时,具有两种发布方式,一种是 AX 方式,也就是插件的方式,可以支持 PB 中各种复杂的控件和对象。另一种方式是 JS 方式,也就是纯 JavaScript 方式,这种方式只能支持有限的 PowerBuilder 特性。

Q: ActiveX插件有多大,会不会每次都下载?
A: Appeon 的插件有点类似于我们常见的 IE 的 Flash 插件。大约 1M 左右的 Cab 包。 IE 会自动下载,并且只是在头一次使用的时候去下载。多个 Appeon Web 应用会使用一个插件,不会去下载多个插件,除非其版本不一样。

Q: 为什么需要架构调整和性能优化?
A: 我们用 Powerbuilder 开发的应用一般是两层应用,也就是 C/S 架构应用。绝大部分代码都是在客户端实现的,用户界面和业务逻辑基本上是交织在一起。
APB 目前主要是实现语言的自动翻译工作,也就是 PowerScript 语言到 JavaScript 语言的翻译,还不能自动分离用户界面和业务逻辑相关的代码。

因此,翻译后的应用如果要运行在 Internet 上,或者低带宽网络环境下,一般还需要进行一些手工调整,将数据存取密集代码和业务逻辑代码转移到数据库服务器或者应用服务器上。这就是所谓的对 APB Web 应用的架构调整和性能优化。
需要说明的一点是,如果翻译后的应用运行在带宽很好的网络环境中,例如带宽 10/100M 的企业网里,可以不做或只做少量的架构调整和性能优化工作。
正阳APB问答

相比APB无奈选择的JavaScript/ActiveX/Blob/J2ee技术栈 ,今天选择JavaScript/React/Json/REST技术栈就顺理成章多了。

 

为了能够清楚地认识PowerBuilder语法,我专门写了一个包含所有语法的过程:数鸭子

  1 public function integer of_count_ducks (string as_ducks) throws exception;
  2 /* for testing of PowerPlume Ps2Js.exe
  3     usage: f_count_ducks("黄鸭子,黑鸭子,白鸭子,黄鸭子,麻鸭子,灰鸭子,小黄鸭,唐老鸭,盐水鸭,南京鸭")
  4     
  5     本过程包括所有PowerScript语法,是编译器开发的the quick brown fox jumps over a lazy dog。
  6 */
  7 
  8 call nonvisualobject::constructor
  9 if super::classname() <> "n_count_ducks" then messagebox("提示", "final类不能被继承")
 10 
 11 n_count_ducks nvo_test; nvo_test = create n_count_ducks
 12 
 13 string ls_info = "门前大桥下,游过一群鸭,快来快来数一数,"
 14 
 15 string errors_arr[7] = {    "warn1:灰鸭子已废弃,请使用麻鸭子","warn2:小黄鸭的规范写法为黄鸭子","err3:唐老鸭已终止授权",&
 16                                 "err4:不得使用死鸭","err5:南京鸭必须死",    "err6:鸭子的数量应不小于八只", "err7:远程调用未知错误"}
 17                                 
 18 string numbers_arr[1 to 8] = {'', '二', '', '四', '', '六', '七', '八'}
 19 
 20 string ducks_arr[16, 2], as_ret
 21 
 22 blob{255} data
 23 integer color
 24 
 25 int counter=0, index=0, li_begin=1, li_current=1
 26 do 
 27     try 
 28         li_begin = li_current
 29         data=blob(as_ducks, EncodingUTF8!)
 30         //Remote Procedure Call
 31         color = nvo_test.static trigger function of_get_instance().of_get_duck(data, li_current);
 32     catch(exception e)
 33         color = 7
 34     finally
 35         index ++
 36     end try
 37     
 38     choose case color
 39         case -1, -2, -3, -4
 40             ducks_arr[index, 1]=mid(as_ducks, li_begin, li_current - li_begin -1)
 41             ducks_arr[index, 2]=""
 42         case 9
 43             goto hack ;// 被攻击暗号
 44         case 1 to 2 
 45             ducks_arr[index, 1]=mid(as_ducks, li_begin, li_current - li_begin -1)
 46             ducks_arr[index, 2]=errors_arr[color]
 47         case 3 to 5
 48             ducks_arr[index, 1]=""
 49             ducks_arr[index, 2]=errors_arr[color]
 50             continue
 51         case 6
 52             ducks_arr[index, 1]=""
 53             ducks_arr[index, 2]=errors_arr[color]
 54             exit //数量小于8
 55         case is >= 7
 56             ducks_arr[index, 1]=""
 57             ducks_arr[index, 2]=errors_arr[color]
 58             exit
 59         case else
 60             goto error
 61     end choose
 62     counter ++
 63 loop while (counter < 8 and index < 16) 
 64 
 65 int i = 1, j = 1
 66 int li_warns=0, li_errors=0
 67 string ls_ducks = ""
 68 do until (j > counter)
 69     if (ducks_arr[i, 1]="") then
 70         li_errors ++
 71     elseif (ducks_arr[i, 2]="") then
 72         ls_info += numbers_arr[j]
 73         ls_ducks += ducks_arr[i, 1] + " "
 74         j++
 75     else
 76         ls_info += numbers_arr[j]
 77         ls_ducks += ducks_arr[i, 1] + "* "
 78         j++
 79         li_warns ++
 80     end if
 81     i ++
 82 loop
 83 
 84 ls_info += ":~r~n"+ls_ducks+"~r~n警告:"+ string(li_warns) +"~t错误:" + string(li_errors) + "。~r~n"
 85 
 86 for i = 1 to 16
 87     if (ducks_arr[i, 2]<>"") then
 88         ls_info += ducks_arr[i, 2]
 89         ls_info += ";~r~n"
 90     end if
 91 next
 92 
 93 date ld_begin, ld_today
 94 time lt_begin, lt_now
 95 
 96 ld_begin=date("2021-4-14") //求日期值的写法
 97 lt_begin=19:18:01
 98 ld_today = today()
 99 lt_now = now()
100 
101 longlong seconds
102 Seconds= daysafter(ld_begin, ld_today)*86400 + SecondsAfter(lt_begin, lt_now)
103 ls_info += "数鸭子代码已发布:"+string(seconds)+"秒。"
104 messagebox("数鸭子过程包含所有PowerScript语法", ls_info)
105 
106 destroy nvo_test
107 return counter
108 
109 hack:
110     halt close
111 
112 error:
113     destroy nvo_test
114     throw create exception
115 
116 end function
View Code

 

PowerBuilder语法大家都熟悉,JavaScript实际上是语法上最接近PB的语言了,毕竟都是90年代的产品,理念也差不多。

至于类与继承,也很像,PB是不支持构造函数的,就是单纯的克隆。JS实际上也是一样,通过原型克隆产生的原型链控制继承。

 

PbXtc(包括PbXrt)开发计划:

分为六个阶段来开发

V0.1.x 编译函数为JS代码片断

V0.2.x 编译窗口为JSX界面代码片断

V0.3.x 编译数据窗口为DataWeb代码片断

V0.4.x 编译数据库访问为Rest访问

V0.5.x 编译事件驱动的完整应用

V0.6.x 编译其它外部代码

 

一般问题解答:

1、转换后,界面的一致性
答:转换时会有选项:严格一致,数据窗口一致,有限弹性,完全弹性四种,一致性由大到小。

2、转换后,本地DLL接口,ocx控件如何处理
答:完全可以兼容,会在本地运行一个微型Web服务,以Rest接口的形式供Web应用调用。

3、是否需要开发配套后端
答:转换时会有选项:完全前端,安全性前端,微前端三种,自动生成的后端代码由少到多,无需开发。

 

 

技术升级

上面这个版本的PbXds最终被否定了,所有代码都推倒重来。

原因是我在开发过程中,一直对这个方案不太满意,主要问题是前端太过Web化,还需要维护JS/GO两个框架。

最终我找到了一条完全后端化的道路,具体内容详见《PowerBuilder现代编程方法X11:PB程序完全跨平台方案

 

<本节完>