js 参数解构_源码级分析!解构HCL Domino Volt低代码工具(二)

#dominoforever

598eefbb871ee2e36b6b7934fc9ba1dd.png

关注我!会有很多干货哟!

另一篇文章:源码级分析!解构HCL Domino Volt低代码工具(一)
今天进入我们这个技术文章系列的重头戏!我们来分析一下Volt的源代码。我们是怎么得到Volt的源代码的呢?来源有两个:
  • Volt应用数据库volt/.nsf中的“资源-文件”。这些文件是Volt应用运行时所需的一些Web资源,例如图片、js,json、css、html等等。获取这些代码很容易,会使用Domino Designer的技术人员很容易获得这些源代码文件的。需要说明的一点是:在“资源-文件”中的文件名称如果是desktop/..../xxxx格式的,我们导出以后会变为desktop_2f_...._2f_....这个样的文件名,这里的“_2f_”代表“/”而已。

  • 我们使用了特殊方式去“窥视”了一下Volt的一些jar包。具体的方法就不说了,懂行的人立马明白,不懂行的人也别追究了。这里必须说明一下:这些源代码是有知识产权的,学习和借鉴过程中,千万不要侵犯HCL的知识产权!

二、技术原理

Volt其实是Domino OSGi的一种技术实现。Domino本身就是应用了大量的Java技术,从IBM Domino R8.5开始就开始把OSGi引入到Domino Web开发中,允许Java程序员利用OSGi对Domino平台进行扩展。我们熟知的XPages技术就是一种OSGi。Volt的核心系统代码就在Domino服务器的osgi/volt/eclipse/plugins目录里面。如下图所示。

3beb5e16537372f5fa16f458bd1e88ab.png

f82a2ac9d37ef911995635774dc17e34.png

在这些Volt的系统代码(就是Java程序代码)中,我们可以看到如下几大类组件:
  • IBM Form Platform Service组件 —— 其实Volt的本源就是IBM Forms,后来叫HCL Leap,把IBM Forms(HCL Leap)改造一下并封装成Domino OSGi应用后就变成了HCL Domino Volt。

  • HCL Domino Leap(dleap) —— 一个Domino OSGi应用,就是HCL Domino Volt的主体程序。

  • 邮件 —— javax.mail API :用于发送邮件。

  • PDF —— Apache PDFBox :用于操作PDF文件。

  • POI —— Apache POI:用于操作Excel文件。

  • Logging —— Apache Common Logging:用于进行日志记录。

  • Apache HTTP Client , Commons IO , Commons File Upload—— 用于实现REST API访问、文件操作、文件上传等HTTP操作。

  • JNA —— Java本地资源访问的JNI的封装。

  • Apache Abdera —— 一个用Java实现的Atom协议,基于HTTP实现Web资源的编辑和发布的协议。现在您明白了为什么要在安装HCL Domino Volt后,需要在Domino服务器的notes.ini文件中加入“HTTPEnableMethods=GET,POST,PUT,DELETE,HEAD”参数了吧。

  • 其它辅助性组件

运行Volt应用所需的Web资源文件中,所有的js文件中的Javascript代码都是基于Dojo框架的。从IBM Domino R8.5开始,Dojo就一直是Domino Web应用所采用的默认的JS框架,官方的XPages和Domino扩展组件(当然也包括Volt)的前台JS框架都是Dojo。找个源代码文件给大家看看就知道了。如下图。

bc968014fe7fac2ae8f58c98233a284f.png

当Domino服务器启动以后,Volt就作为Domino OGSi应用随Domino启动并加载。所有Volt的功能均由hleap这个OSGi应用提供服务。当你访问一个Volt应用时,系统会根据volt/VoltBuilder.nsf中的应用列表找到 volt/.nsf 进行数据操作,而Volt应用的用户界面则是由Volt提供,仅仅把  volt / .nsf 当做一个数据库使用。但是如果需要展现数据,就需要使用 volt/.nsf 里面的Notes视图来定义前台如何显示数据表格(不是直接在Web端显示Notes视图,而是用Notes视图作为一种显示格式的定义,比如有几列,每列显示什么内容,同时读取Notes视图的数据REST API来读取数据)。 二、1、前台代码分析我们需要重点分析的是Volt应用数据库中的“资源-文件”中desktop/xxx.js 这样的JS文件里面的代码。这种JS文件里面是一个前台显示的Volt表单的JS。因为这样的JS代码是开放的,我们进行分析的顾虑就小了很多。我们使用VS Code打开这样的JS文件。哈哈!原来我们看到的Volt表单里面最主要的部分在这里定义的。见下面两个图。第一张图是Volt表单界面,第二张图是这个Volt表单对应的JS文件。

69223b11fa1a4d371859096e78e69ae3.png

Volt表单范例:我们看到的界面。

031ebaa565215daa7a1a01b17c3686a9.png

Volt表单对应的JS文件。

我们看一下在JS文件中定义的Volt表单的主体的HTML代码部分。主体部分是dojo.declare声明的——你设计一个Volt表单时有几个页面(Page),就有几个dojo.declare。每个dojo.declare定义一个页面(Page)的HTML主体部分。下图是一个Volt表单的一个页面(Page)的定义。

cee3c2bafe4270bbb24f37ad77cd29b6.png

Volt表单中每一个页面的JS定义。

我们可以看到键值“templateString”就是这个Volt表单的一个页面的主体HTML代码。但是HTML代码中还有很多动态参数定义。这些动态参数定义将在前端被替换为实际的内容。我们摘取其中一段来讲解一下。
<label id=\"${_uid}F_Paragraphtext1-label\" for=\"${_uid}F_Paragraphtext1-widget\" class=\"lfFormLabel\">多行条目label>\n<div class=\"hint lfFormFieldHint\" id=\"${_uid}F_Paragraphtext1-hint\" style=\"display: block;\">这里是您的个人简介div>
这里面的${_uid}将被替换为真实的Uid。见下图。

ed32c1d69481886887d3bfe06e238b73.png

那么这个Uid是怎么来的呢?在哪里定义的呢?我在这里先提及一下。来自于"资源-文件"中的application.xml。后面我们会单独分析这个文件。这个文件太重要了,是一切Volt系统程序的起点。上一篇文章说过,当你用Volt设计应用时,所有的设计元素,表单、页面、表格、输入框……全部都会自动被标识上一个Uid。如果你在设计Volt表单时还使用了服务(Service),那么这些内容的前台定义都在该表单名称相关的html,js,json中。具体的就不细讲了。我们还要关注的是类似于“desktop/widget/F_xxx_P_xxx.html”这样的html文件。我们打开看一看,这里面是啥玩意。

871d7635871d99426624a69764f127a7.png

我的天啊,里面全部是基于dojo的HTML代码啊。但是请注意,里面还是有${_uid}这样的动态标签,会被替换成真实的Uid的内容。下面,我们看一看Volt表单在浏览器端的代码是啥样的。可以说,全部是通过dojo与后台Volt系统交互出来的HTML代码。就说一点:就是前台的dojo去访问后台的这些Web资源文件(通过Volt代码,不是直接访问Notes数据库的文件资源)由前台JS动态生成的Web界面。

a482a16bd18a43793ce415ff5008df9a.png

在此范例的Web源代码中,最重要的一句话是:
if(FREEDOM.theMainFormName != null)    {         dojo.require("freedom.widget.solution.environment.CurrentItemView");         dojo.require("freedomapplication._92fc4c93_1c65_42de_85a2_41f6f6ad273e");         dojo.require('freedom.client.DataStore');         dojo.require('freedom.client.SimpleController');     }

看到那个Volt应用的Uid了吗?这就是一切Volt程序的起点。通过这个Uid,系统自动找到Volt应用数据库(volt/92fc4c93_1c65_42de_85a2_41f6f6ad273e.nsf)然后后台加载数据库中的“资源-文件”,返回到前台后由dojo在动态生成表单界面。

另外,我们发现了一个动态加载第三方JS的代码。虽然这个JS目前没有被加载,可能用于访问分析,但是还是建议HCL去掉这种敏感性的东西。

4357c5d2e8f50ffe7070d6b3f5f89cdd.png

我们总结一下前台代码分析的结果:
  • 前台Volt表单显示并不是使用的Notes表单直接显示的,而是通过Volt系统动态生成的。

  • 当我们启动一个Volt应用显示我们设计好的表单界面时,Volt会从Volt应用数据库中的“资源-文件”中加载表单对应的html,js,json,css等Web资源文件,在处理以后发送给前台的dojo。

  • Volt在应用数据库的“资源-文件”中缓存了该Volt应用的前台的UI代码、js代码,css代码,这样做其实是加快了后台处理速度。(这点值得学习)

  • 至于那些"desktop/....html,js,json"是到底是什么样的关系,起到什么作用,只有您亲自去研究了。我就不讲了,嘿嘿……(这也是我们的一点点私心嘛,请您理解哟)。提示一下:前台看到一段代码,就反推回去,再对照“资源-文件”中的那些文件,看看里面的内容,就会搞明白了哟。

为什么要要研究Volt的前台代码的技术原理呢?因为你可以改那些资源文件嘛。改一改就知道Volt前台的用户界面也是可以从底层就被您换掉的喽。这也属于Volt应用深度定制的一种方法呀。

二、2、后台Java代码分析

终于到了揭开Volt真面目的时候了。我们的研究方法仅限于学习和借鉴的目的,千万不要去侵犯HCL的知识版权!切记!切记!你可以做copycat,但是别做小偷!

在IBM Forms(HCL Leap)中,数据存储都是基于后台关系型数据库的,例如Oracle,DB2之类的。在HCL Domino Volt提供的那些jar包中有相当一部分是使用的java.sql或javax.sql.DataSource。看到这里懂Java技术的人就明白了,IBM Forms(HCL Leap)会使用JDBC/JNDI/数据库连接池之类的技术和后台数据库进行交互。

ea36ecc720d7dda412cd9274946bd394.png

HCL Domino Volt使用了相当一部分IBM Forms(HCL Leap)的代码。这一点从下图的文件列表就可以看出来。ibm.fsp.*,ibm.nitro.*,这些jar包基本上都是IBM Forms(HCL Leap)的产品的程序包。

3beb5e16537372f5fa16f458bd1e88ab.png

但是,HCL Domino Volt是使用的Notes数据库直接通过Notes Java API和对Notes API的JNI调用进行数据操作的。我们通过对 "dleap.util.NSFUtils"、“dleap.util.JNIUtils”和“dleap.util.JNAUtils”的源代码分析就可以得到这个结论。

dleap.util.JNIUtils里面引用了Domino本地函数调用。com.ibm.designer.domino.napi.*类似于调用Domino Designer的功能,可以完成对Notes数据库和Notes设计元素的本地C API方法的调用。

import com.ibm.designer.domino.napi.NotesAPIException;import com.ibm.designer.domino.napi.NotesCollection;import com.ibm.designer.domino.napi.NotesCollectionEntry;import com.ibm.designer.domino.napi.NotesDatabase;import com.ibm.designer.domino.napi.NotesHandle;import com.ibm.designer.domino.napi.NotesNote;import com.ibm.designer.domino.napi.NotesSession;import com.ibm.designer.domino.napi.util.NotesIterator;import com.ibm.designer.domino.napi.util.NotesUtils;import com.ibm.domino.napi.c.BackendBridge;

974145b751076ea99cdc0e65afd54191.png

现在您就应该明白了:我们在发布(部署)一个Volt应用时,Volt会创建一个Volt应用数据库(volt/.nsf)并在这个数据库中自动创建对应的Notes表单、子表单、视图,“资源-文件”。当你访问一个Volt应用时,Volt会在后台读取Volt应用数据库的一些资源文件,会在数据库中进行数据操作(大多数通过RESTful方式)……这些都是通过JNA(Notes API JNI)实现的。

这个过程其实和IBM Forms(HCLLeap)有大同小异:IBM Forms(HCL Leap)是通过JDBC/JNDI/数据库连接池操作后台关系型数据库,Volt是通过Notes Java API和Notes API JNA操作Notes数据库。

说到这里说一个题外话:很多人说Notes数据库是封闭的,不会对它进行数据操作,其实你可以用Notes Java API啊。自己不学习,还要抱怨Notes数据库本身,你这样和“自己不会XXOO,还想制造人类”有啥区别!c1c6822f5743bbd9f14466bad1a0cbf6.pngc71b7c74ac5b99890e5509011682cded.pngc71b7c74ac5b99890e5509011682cded.pngc71b7c74ac5b99890e5509011682cded.png

我们再看看Volt源代码的常量定义部分,看看有哪些技术上的猫腻。

package dleap.util;public abstract interface DominoConstants{。。。  public static final String VOLTCONFIG_ALL_SETTINGS_VIEW = "lkp-SettingsbySettingName";  public static final String VOLTCONFIG_CRED_ALIAS = "CredAlias";  public static final String VOLTCONFIG_CRED_PASSWORD = "Password";  public static final String VOLTCONFIG_CRED_USER_NAME = "UserName";  public static final String VOLTCONFIG_CRED_VIEW = "ServiceCredentials";  。。。  public static final String VOLT_VIEW = "VoltView";  public static final String ALLDOCS_VIEW = "VoltView";  public static final String CAT_VIEW = "CatView";  public static final String VOLT_FORM = "VoltForm";  public static final String VOLT_FORM_VIEW = "formView";  public static final String VOLT_SUBFORM_VIEW = "subformView";  public static final String VOLT_STAGE_HASH_VIEW = "(FlowHashLookup)";。。。  public static final String DIRECTORY_USER_LOOKUP_INDEX = "($Users)";  public static final String DIRECTORY_GROUP_LOOKUP_INDEX = "Groups";  public static final String VOLTAPP_BUILDER_PATH = "volt/VoltBuilder.nsf";  public static final String BUILDER_APPOWNER_BYAPPNAME_VIEW = "(appsByOwnerByNameLookup)";  public static final String BUILDER_APPOWNER_BYUPDATE_VIEW = "(appsByOwnerByUpdatedLookup)";  public static final String BUILDER_ALL_BYAPPNAME_VIEW = "(allAppsByNameLookup)";  public static final String BUILDER_ALL_BYUPDATE_VIEW = "(allAppsByUpdatedLookup)";  public static final String BUILDER_ALL_BYID_VIEW = "(allAppsById)";  public static final String BUILDER_FILES_BYID_VIEW = "(filesById)";  public static final String BUILDER_FILES_BYAPPID_VIEW = "(filesByAppId)";  public static final String BUILDER_FILES_PARENTUNID_VIEW = "(filesByParentUnid)";。。。}

果然如我们所预见的那样,Volt去volt/VoltBuilder.nsf中通过Notes视图去读取应用列表的。处于设计阶段的Volt应用也是存储在volt/VoltBuilder.nsf中的。

另外,在Volt 1.0.1.9中,可以在Domino目录中选择用户,看来也是通过读取names.nsf中的Notes视图($Users)和Groups实现的。

下面是一些关键的信息。如果您读懂了,也就读懂了;没有搞明白,别深究了。

下面是dleap.util包中的几个类。通过对这些类的源代码的研究,我们更加明白了Volt是如何操作Notes数据库的。

  • dleap.util.NSFUtils:Notes Java API操作Notes数据库。
  • dleap.util.JNAUtils / dleap.util.JNIUtils / dleap.util.DDesignDBManager:Notes API 本地调用操作Notes数据库。
  • dleap.util.DXLUtils:通过DXL操作Notes数据库。
  • dleap.util.LeapUtils:把Volt表单的字段定义与Notes域进行映射。
  • dleap.util.DCRUDUtils:通过DQL读取Notes数据、通过CRUD上传和下载Notes文档中的附件、Notes文档的数据变成键值对(JSON)等等。

为了保护HCL知识产权,我们这里就简单地窥视一下dleap.util.NSFUtils的源代码。

db1f3fd40b02029e1a5ae7220bc14aff.png

8b0fe391b1a9af79804ab05e44d000b2.png

整个dleap包的类结构如下图所示。我们后面再列出一些关键的信息给大家。

8a0ede36a7f2d902eeabab11edc8b682.png

为了不透露太多HCL的核心代码,我们下面就列出重要的一些信息给大家。如果有机会,您可以自己慢慢研究。以我们目前的研究成果,dleap包里面确实是包罗万象,对我们有很大的启发作用。大公司就是大公司,能够从源代码级别学习人家的产品设计和研发方法,真的是一种“找罪受的享受”。哈哈哈哈……

  • dleap.FSPServlet / dleap.Activator :加载Volt主界面。

  • dleap.util.*:包含了对Notes数据库的各种操作。

  • dleap.impl.credentials.*:调用外部服务时的用户凭证存储操作。

  • dleap.impl.data.*:Volt前台JS API的BO类的后台处理。

  • dleap.impl.datastore.*:存取Notes数据库。这里替换了HCL Leap对关系型数据库的存取,使用了Notes数据库存取数据。

  • dleap.impl.endpoints.*:前台读取Notes数据库数据时的后台处理,主要是返回JSON。

  • dleap.impl.nl.*:语言资源。怪不得支持那么多种语言呢。

  • dleap.impl.service.*:把能够号称为service的都放到这里了。自己看截图吧。

5e1fab17e698a110527e2ce94b1cf8ac.png

说不上来为什么这么安排类路径。反正能叫service的都在这里。

  • dleap.impl.services.directory.*和dleap.impl.services.dominoservice.*:目录服务,用于读取用户目录的。

  • dleap.impl.storagefile.*:文件的流操作。磁盘文件的生成都是在这里的。

另外,ibm.nitro.*和ibm.fsp.*都是来自于IBM Forms(HCL Leap)。通过对这些程序包的源代码的初步研究,我们可以得出一个结论:Volt的数据展现模型是前后台搭配实现的,前台实时地动态拉取和显示,实现了类似于MVVM的数据模型,只不过前台用的是dojo,没有用现在流行的react,angular,vue而已。

Volt自身包含了对Excel和PDF的操作,这些都是通过POI和PDFBox实现的。懂得Java技术的读者应该都知道是怎么操作的了。

二、3、application.xml

这个文件是一切Volt程序的起点。因为在这个文件里面定义了一个Volt应用的全部配置信息。

在这个XML文件中,配置信息定义分成几个部分:

bindings:字段和Volt表单的绑定关系。

files:Volt应用的文件资源,例如表单上的图片等。

setvices:状态和服务定义。

types:每个字段的定义信息。

ui:Volt应用的表单的界面的定义。结构是ui-form-page-gridLayout-rows-row-cell。

篇幅关系,我们就着重讲一讲ui部分。很多人也很想知道Volt设计出来的表单是怎么所见即所得的。

先看看Volt设计器的界面。这里你会发现Volt设计表单的时候,是按照行和列的模式进行的。所有设计元素,例如字段,表格,区段、Tab页等都放在每一行的单元格里面。

16cdbd1cb7637596ee29970eea2a6235.png

在Volt的后台存储的配置信息里面,就是按照设计器里面的行列模式存储的。如上图的两行两列四个单元格,在application.xml里面,就是:

            ...这里是字段信息...                                    

我们截取一部分cell代码,可以分析一下这个单元格是如何显示内容的。下面是中一段代码:

<cell>    <textField length="medium" placeHolder="" seenInOverview="true" isUppercase="false" formatRegEx="false" name="A_Uername" uid="cab9544b-ef34-4a89-8935-2df9d9e06e99" customCssClasses="" customAttribute="">        <label>姓名label>        <description>description>        <hint>请输入您的姓名,必须填写!hint>        <events>            <event name="onClick" serviceId="41033924-ec60-4241-808f-cb5b3488e7b0" formulaId="">event>        events>    textField>cell>
这个单元格里面定义了一个textField,标题是“姓名”,描述为“”,提示信息是“请输入您的姓名,必须填写”。那么我们怎么知道这个textField的类型呢,该是什么样的显示方式呢?是单行文本框,多行的,下拉列表还是复选框呢?我们需要通过这个textField的Uid属性去到types里面查找它的类型定义。
<string length="50" isUppercase="false" formatRegEx="false" name="A_Uername" uid="cab9544b-ef34-4a89-8935-2df9d9e06e99" required="true" unique="false" dataLabel="" customAttribute="">    <label>姓名label>    <description>description>    <defaultValue>defaultValue>string>
我们看到该字段是string,长度是50,没有format属性(说明是单行文本框),required属性是true,说明:
  • 要生成一个的HTML代码。

  • 需要在提交时判断这个数据框是否为空,因为required="true",是必填的字段。

  • 因为有提示文字,所以该输入框下面会有一段说明文字。

Volt应用前台的显示效果如下。

6f46667a246e897c723c12805a6b8345.png

前面我们说过,Volt给所有的设计元素都标识了一个Uid,这个Uid就是在XML文件、JSON、前后台代码中作为该元素的唯一标识使用的。在application.xml中,这样的代码会很多,有兴趣的读者可以仔细研究一下。另外,Volt前端表单的布局采用的是html table布局方式,通过dojo和css实现了自适应调整。但是我还是建议采用div布局比较好。 三、总结

通过对Volt前后台源代码的分析,我们发现Volt的技术原理并不复杂。其实我们也了解了IBM Forms(HCL Leap)的工作原理。

总结一下Volt的技术原理。

  1. Volt设计器中进行应用设计时,所有的信息都会存储到Notes数据库中。
  2. Volt应用发布(部署)以后,Volt会通过JNA方式创建Notes数据库volt/.nsf。并通过JNA/JNI、Notes Java API等创建对应的数据结构,例如表单、子表单和视图等。同时在Volt应用数据库中以“资源-文件”的方式存储Volt应用的配置信息、前台JS,HTML,CSS等。
  3. 访问一个Volt应用时,Volt从该数据库中读取配置信息,资源文件和数据。主要的技术是JNA/JNI、DQL、Notes Java API。
  4. application.xml里面定义了一个Volt应用的所有的数据结构和表单界面。
  5. HCL Domino Volt的原型就是IBM Forms(HCL Leap)。只不过是变成了Domino OSGi应用而已,数据库使用的Notes数据库。

我们最新推出的JoinHand DomPortal Volt集成工具,就是对Volt源码级的解构的最新成果。这个工具是全球第一个,也是目前唯一一个实现Volt低代码工具与专业代码工具进行集成的解决方案!JoinHand DomPortal —— Domino应用的创意未来!

恭喜你,当你读到这里的时候,差不多阅读了6500个文字。加上我们的第一部分《数据结构》(点击即可阅读),差不多洋洋洒洒10000多个文字了。非常感谢您的阅读!

另一篇文章:

源码级分析!解构HCL Domino Volt低代码工具(一)

关注我,会有很多干货哟。我是持续创新的Domino开发者! 598eefbb871ee2e36b6b7934fc9ba1dd.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值