71. 从Lotus Notes表单到XPage——兼谈程序里的二进制文件和文本文件

<style type="text/css">p {font-family:Microsoft YaHei;}</style>

本文对比分析了LotusNotes平台上两种视图技术。架构和运转过程的分析特定于Lotus Notes平台,其后的关于二进制文件和文本文件的讨论则具有普遍性。(严格地说,XPages指的是IBM基于Lotus Domino和JSF的快速开发技术,XPage则指单个页面,两者的首两个字母都大写。笔者一直遵守这个拼写上的规则。)

架构

XPages的引入是Lotus Notes作为一个开发平台二十多年的历史中最具革命性的变化。从架构上讲,最根本的变化就是把视图和数据定义分离开。这里的视图不是指Notes视图,而是应用程序中展现给用户的界面部分。在传统的Lotus Notes应用程序里,表单既是用户界面,又负责定义字段。这样虽然使视图设计和数据设计的工作合二为一,加快了开发速度,但是视图和数据过于紧密不可拆分的联系大大束缚了系统的结构,不仅减少了灵活性,而且很多时候成为负担。新的XPage则和后端的数据设计没有直接的关系。表单退化成仅仅定义各种类型的文档的字段,也就是定义应用程序用到的各种数据实体存储在数据库时对应的容器的规范,类似于基于关系型数据库的应用程序里的表定义。设计表单时不用考虑用户界面,可以使这些“表”设计得更紧凑、合理、高效。另一方面,以XPage作界面解脱了与后端数据库的结构的捆绑,能够专心适应用户对界面的要求,免去了原来每个表单后面拖着的文档的负担,并且可以接合更广泛的数据类型,实现更灵活的处理。

运转过程

除去架构上的优点,XPage作为一种视图技术与Lotus Notes表单也有很大的差异。从运转过程来说,在一个典型的客户端服务器环境的应用程序里,开发人员设计的表单直接以二进制格式存储于服务器上的某个数据库里,随用户打开文档的操作被传输到Notes客户端,然后由客户端解释,最终展现在用户眼前;XPage页面对应的是XML格式的文本文件,XPages设计时引擎首先将它一一对应转化成Java源文件,然后编译成字节码的class文件,用户从浏览器请求某个页面时,XPages运行时引擎装入该XPage对应的class文件,执行生成前端文件(包括HTML、CSS和JavaScript等),返回给浏览器,最后浏览器再解释这些文件将页面呈现给用户。

二进制文件与文本文件

上面提到表单和XPage在存储格式上的不同,这也是两种技术本质上的重大差别。表单和其他传统的设计元素一样,都是以Lotus Notes私有的“复合数据”(Composite Data简称CD)二进制格式存储在数据库里,只能用Designer和Notes客户端这些特定的软件写入、读取和识别(NotesPeek是IBM提供的一个可以一窥LotusNotes数据库和各个设计元素内部组成的小工具)。XPage则是以文本格式保存(当然也是被包容在一个Lotus Notes数据库的基本存储单元note内)。这个二进制与文本格式的差异意味着什么呢?

首先是文件的编辑方式。二进制文件因为对人是不可读的,只能借助于专门的软件读取和修改。此软件将二进制的代码转换成对人有意义的形式,如属性,再将在此形式下的修改转换回二进制代码保存。文本文件则使用任何文本编辑器都能阅读和修改。在涉及到诸如表单和XPage这样的界面文件时,现代的开发工具基本上都装备了可视化的编辑器,用户只需在视图上添加控件,拖拉位置和修改属性就能完成界面设计。似乎背后采用的是二进制还是文本格式关系不大。但是能够以文本形式编辑在某些场合仍然更加便利。比如要批量修改多个对象的属性时,在可视化的编辑环境里使用属性框时,有两种途径可循。第一种是很多开发环境都采用的,即选中多个对象后,在属性框里允许有相同值的属性处可以统一地编辑,新的值会被应用到所有选中的对象。Domino Designer在同时修改多个设计元素的属性(如继承的模版、是否允许设计变更和刷新更新等)时就是如此。第二种方法比较少见,是将某一个对象的某一种属性的当前值复制到其他同类对象上。Designer在编辑视图时,很多视图列的属性就可以这种方式应用到所有列上。

其次是文件的可移植性。二进制文件与所在机器采用的底层编码的某些特性紧密相关,如字节顺序(endianess)、数据对齐(data alignment)和字节填充(byte padding),因而文件从一台机器移植到另一台机器,就可能要做相应的转换。文本文件因为每个字符都具有跨平台的明确含义,并且占用的字节数也是固定的,跨机器的文件复制对用户来说是完全透明的。只要使用一致的字符编码,在任何机器上读写文本文件都没有差别。具体到Lotus Notes的环境,传统的设计元素采用的二进制格式在不同平台的存储器里遵循的规则就可能不同。例如,运行在Intel x86机器上的Lotus Notes客户端和运行在基于POWER指令集的机器上的LotusDomino服务器,一个数据库里的设计元素在这两台机器的存储器里就具有不同的字节顺序【注1】。所以Lotus Notes读写数据库的API规定了不依赖于底层机器的数据标准,各个平台上的不同的Lotus Notes/Domino版本的本机代码负责在所在平台的底层编码和这个标准之间转换【注】。XPage和生成的Java文件都是文本格式的,先天就更容易在不同平台间移植,就像其他常用的跨平台的web开发的脚本语言一样。

第三是文件的版本控制。在比较版本之间的差异,合并分支版本等版本控制的常见任务上,文本文件都比二进制文件容易很多。Lotus Notes平台上迟迟出现的版本控制功能便是利用DXL将设计元素从二进制格式转换成XML的文本文件,其中的XPage相关文件就省去了转换的步骤。

注1:Intel x86平台的字节顺序是little endian,POWER的是big endian。

注2:参看Lotus C API for Domino and Notes的TYPE_xxx - Item Data Type definitions.、ODSReadMemory - Convert a structure from canonical format to machine-specific format.、ODSWriteMemory - Convert a structure from machine-specific format to canonical format.三页。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
XPAGE学习笔记 1 Theme 2 2 在Xpage中使用Dojo 2 2.1 加载dojo.js 2 2.2 设置应用程序主题引入tundra.css 2 2.3 设置xpage属性引入dojo模块 3 2.4 Xpage的页面html代码 4 2.5 在xpage中使用dojo编程 4 2.5.1 按钮(dijit.form.Button) 4 2.5.1.1 通过插入<div>标签生成dojo按钮 5 2.5.1.2 使用xpage按钮控件 7 2.5.1.3 在按钮控件上使用事件处理程序控件 7 2.5.2 Xpage中使用 Spinner(dijit.form.NumberSpinner) 9 2.5.3 在xpage中使用ProgressBar(dijit.ProgressBar) 10 2.5.4 在Xpage中使用树(dijit.Tree) 10 2.5.4.1 JSON与XML数据源 11 2.5.4.2 Box树与Order树 12 2.5.4.3 对树进行添加、删除及重命名操作 13 2.5.4.4 将树的操作记录到后台数据库 14 2.5.4.5 树的拖放 15 3 在Xpage中如何查找控件 16 3.1 理解document.getElementById 16 3.2 理解dojo.byId 17 3.3 理解dijit.byId 17 3.4 理解XSP.getElementById 17 3.5 理解getComponent 17 4 Xpage VS 18 5 使用oneui主题 18 6 参考 18 6.1 JSON与String转换 18 6.2 XPage中调试代码 18 1 Theme 主题在Domino中的路径:D:\IBM\Lotus\Domino\xsp\nsf\themes 2 在Xpage中使用Dojo  Xpage自动加载dojo.js  通过设置应用程序主题将tundra.css引用到xpage  设置xpage属性,引用dojo模块  在xpage中使用dojo编程 2.1 加载dojo.js Xpage会自动加载dojo.js,默认情况下xpage自动查找data\domino\js下的最新版本。也可以通过设置xsp.properties文件来修改dojo版本,重命名xsp.properties.sample 文件(D:\IBM\Lotus\Domino\data\properties\ xsp.properties.sample) 2.2 设置应用程序主题引入tundra.css 通过设置应用程序主题将tundra.css引用到xpage 2.3 设置xpage属性引入dojo模块 2.4 Xpage的页面html代码 2.5 在xpage中使用dojo编程 2.5.1 按钮(dijit.form.Button)  通过插入<div>标签生成dojo按钮  使用xpage按钮控件  在按钮控件上使用事件处理程序控件 2.5.1.1 通过插入<div>标签生成dojo按钮 第一步:在xpage中插入<div dojoType=”dijit.form.Button” id=”dojoBtn”></div> 第二步:在xpage中插入<xp:scriptBlock></xp:scriptBlock> 第三步:编写客户端javascript <div dojoType="dijit.form.Button" id="dojoBtn"></div> <xp:scriptBlock> <xp:this.value><![CDATA[function showDia(){ var dia = new dijit.Dialog({title:"dojo按钮演示",style:"width:150px"}); var pane = document.createElement("div"); pane.innerHTML = "<span>Hello Dojo!</span>"; dia.setContent(pane); dia.show(); } function btnDemo(){ var dojoBtn = dijit.byId("dojoBtn"); var dojoBtnNode = dojo.byId("dojoBtn"); dojoBtn.setLabel("Dojo Button"); // dojo.connect(dojoBtn.domNode,"onclick","showDia"); dojo.connect(dojoBtnNode,"onclick","showDia"); } dojo.addOnLoad(btnDemo);]]></xp:this.value> </xp:scriptBlock> 2.5.1.2 使用xpage按钮控件 <xp:button id="button1" value="按钮控件"> <xp:this.onclick> <![CDATA[ var dia = new dijit.Dialog({title:"提示:",style:"width:150px;"}); var pane = document.createElement("div"); pane.innerHTML = "<span>dojo按钮演示!</span>"; dia.setContent(pane); dia.show(); ]]> </xp:this.onclick> </xp:button> 2.5.1.3 在按钮控件上使用事件处理程序控件 将事件处理程序控件拖放到按钮控件上 2.5.2 Xpage中使用 Spinner(dijit.form.NumberSpinner) <xp:inputText id="numSales"></xp:inputText> <xp:scriptBlock> <xp:this.value><![CDATA[ dojo.addOnLoad(function(){ new dijit.form.NumberSpinner({ name:"#{id:numSales}", value:50, smallDelta:1, min:0, places:0},XSP.getElementById("#{id:numSales}")); }); ]]></xp:this.value> </xp:scriptBlock> var myvalue = "#{javascript: var value; if(getComponent("numSales").getValue() != null){ value = getComponent("numSales").getValue().intValue(); }else{ value = 50; } return value.toFixed(0);}"; new dijit.form.NumberSpinner({name:"#{id:numSales}", value:myvalue, smallDelta:1, constraints:{min:0,places:0}}, XSP.getElementById("#{id:numSales}")); 2.5.3 在xpage中使用ProgressBar(dijit.ProgressBar) <div dojoType="dijit.ProgressBar" style="width:300px" jsId="jsProgress" id="div1"> </div> <xp:button value="Start" id="button2"> <xp:eventHandler event="onclick" submit="false"> <xp:this.handlers> <xp:handler type="text/javascript"> <xp:this.script><![CDATA[ var numParts = Math.floor(100/7); jsProgress.update({ maximum: numParts, progress:0 }); for (var i=0; i<=numParts; i++){ setTimeout("jsProgress.update({ progress: " + i + " })",(i+1)*100 + Math.floor(Math.random()*100)); } ]]></xp:this.script> </xp:handler> </xp:this.handlers> </xp:eventHandler> </xp:button> 2.5.4 在Xpage中使用树(dijit.Tree)  JSON与XML数据源  Box树与Order树  对树进行添加、删除及重命名操作  将树的操作记录到后台数据库  树的拖放 2.5.4.1 JSON与XML数据源 box.json文件 { identifier:'id', label:'name', items:[ {id:1,name:"盒子1",type:"box",weight:0} ] } order.xml文件 <orders> <order> <orderNumber>001</orderNumber> <description>订001</description> <priority>航运</priority> <line> <orderNumber>001-1</orderNumber> <qty>1</qty> <sku>11761</sku> <description>精通DOJO</description> </line> <line> <orderNumber>001-2</orderNumber> <qty>3</qty> <sku>11789</sku> <description>项目管理那些事儿</description> </line> </order> <order> <orderNumber>002</orderNumber> <description>订002</description> <priority>快递</priority> <line> <orderNumber>002-1</orderNumber> <qty>10</qty> <sku>11763</sku> <description>精通J2EE</description> </line> <line> <orderNumber>002-2</orderNumber> <qty>30</qty> <sku>11799</sku> <description>超越对手</description> </line> </order> </orders> 2.5.4.2 Box树与Order树 <div dojoType="dojo.data.ItemFileWriteStore" jsId="boxStore" url="/tree/box.json"> </div> <div dojoType="dojox.data.XmlStore" jsId="orderStore" label="description" keyAttribute="orderNumber" url="/tree/order.xml"> </div> <div dojoType="dijit.tree.ForestStoreModel" jsId="boxModel" store="boxStore" rootLabel="退货物品" childrenAttrs="orders,items"> </div> <div dojoType="dijit.tree.ForestStoreModel" jsId="orderModel" store="orderStore" rootLabel="订" childrenAttrs="line,order"> </div> <xp:td style="height:200.0px" valign="top"> <xp:panel> <div dojoType="dijit.Tree" id="boxTree" model="boxModel"> <script type="dojo/method" event="onClick" args="item"> lastBoxSelected = item; </script> </div> </xp:panel> </xp:td> <xp:td style="height:200.0px" valign="top"> <xp:panel> <div dojoType="dijit.Tree" id="orderTree" model="orderModel"> </div> </xp:panel> </xp:td> 2.5.4.3 对树进行添加、删除及重命名操作 <xp:table> <xp:tr> <xp:td> <div dojoType="dijit.form.Button" id="addBtn"> 添加 <script type="dojo/method" event="onClick"> boxStore.newItem({id:2,name:"盒子2",type:"box",weight:0,orders:[]}); </script> </div> </xp:td> <xp:td> <div dojoType="dijit.form.Button" id="delBtn"> 删除 <script type="dojo/method" event="onClick"> if(boxStore.isItem(lastBoxSelected)){ boxStore.deleteItem(lastBoxSelected); } </script> </div> </xp:td> <xp:td> <div dojoType="dijit.form.Button" id="reBtn">重命名 <script type="dojo/method" event="onClick"> if(boxStore.isItem(lastBoxSelected)){ boxStore.setValue(lastBoxSelected,"name","新盒子"); } </script> </div> </xp:td> </xp:tr> </xp:table> 2.5.4.4 将树的操作记录到后台数据库 <div dojoType="dojo.data.ItemFileWriteStore" jsId="boxStore" url="/tree/box.json"> <script type="dojo/connect" event="onNew" args="newItem"> var itemId = this.getValue(newItem,"id"); var itemName = this.getValue(newItem,"name"); dojo.xhrPost({ url:"agCreateNewBox?OpenAgent", content:{id:itemId,name:itemName}, timeout:10000, error:function(){alert("对不起,出错啦!");}, load:function(){alert("数据保存成功!");console.debug("数据保存成功!");} }); </script> <script type=”dojo/connect” event=”onDelete” args=”delItem”> </script> </div> 2.5.4.5 树的拖放 要使树具有拖放功能,仅仅需要设置dndController属性(dijit._tree.dndSource已废弃): 要限制拖放的行为,仅仅需要实现checkAcceptance或checkItemAcceptance处理函数即可: 以上已经实现了树的拖放功能了! 但是,如果要将拖放的结果保存起来,也就是说要改变树的模型并将模型数据保存,那么应该编写onDndDrop的处理函数(通过增加jsId获得dndController会出现this.avaria is null 错误,所以下面通过小部件初始化时将dndController传递给变量controller): 在boxDrop中与checkAcceptance一样可以得到target与source,另外,onDndDrop中的source与nodes有些相似source.selection[something]=nodes[somethin] 结合ItemFileWriteStore 可以实现对模型数据的保存 3 在Xpage中如何查找控件 3.1 理解document.getElementById 得到node,创建node可以用document.createElement 这样访问或设置节点的所有属性 node.innerHTML = “” node.value=”” var id = node.id var style = node.style 3.2 理解dojo.byId 得到node,也可以通过dijit.byId().domNode得到节点 dojo.byId(“myid”).value dojo.byid(“myid”).id dojo.attr(node,attribute) 如读取dojo.attr(node,”id”) dojo.attr(node,attribute,value) 如设置dojo.attr(node,”id”,”myid”) dojo.hasAttr(node,attribute) 如判断 dojo.hasAttr(node,”id”) dojo.connect(node,”onclick”,handler) dojo.removeClass(node,”myclass”) 3.3 理解dijit.byId 得到dojo控件对象,也可以通过dijit.byNode(dojo.byId())得到对象 不同的控件的getValue()返回值类型不一样 在xpage中对于<div>标签可能使用期dijit.byId(“myid”),对于<xp:>标签是不可以的 dijit.byId().getValue() dijit.byId().setValue() 3.4 理解XSP.getElementById 这是XSPClientDojo 库中定义的,XSP全局变量相当于document对象,为客户端访问xpage控件提供方法 XSP.getElementById(“#{id:myid}”)使用JSF达式查询控件 var xid = XSP.getElementById(“#{id:myid}”) xid.value = “myvalue” xid.name = “myname” xid.disabled = false 3.5 理解getComponent javascript服务器端访问控件 getComponet(“myid”).getValue(); getComponet(“myid”).getSubmittedValue(); getComponent(“myid”).setValue(); getComponent(“myid”).setSubmittedValue(); 4 Xpage VS 5 使用oneui主题 6 参考 6.1 JSON与String转换 dojo.fromJson(string) 返回json对象 dojo.toJson(object) 返回string对象 eval(‘(‘+string+’)’) 返回json对象 6.2 XPage中调试代码 window.alert 函数不可用于服务器脚本。 使用以下函数写入服务器上的 console.log(例如 C:\Notes\Data\IBM_TECHNICAL_SUPPORT\console.log): print(string) 写入一个字符串。 _dump(object) 写入示对象的字符串。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值