XPage 基本调试技巧

XPage 是 IBM Lotus Notes/Domino 8.5 为 Lotus Domino Web 2.0 应用程序开发人员提供的新技术。与以往发行版相比,它们提供了更大的灵活性,并且更好地控制交付给 Web 浏览器客户机屏幕的内容。本文主要是介绍 XPage 服务器端 JavaScript/Java 调试的基本技巧,以便 XPage 初级开发者有更多时间专注 XPage 本身的开发,而不是在如何调试上。

XPage 调试主要分为客户端和服务器端代码的调试,本文将主要讲解服务器端代码的调试,同时也将介绍如何更方便的查看各种错误信息,以及各种常见错误信息出现的原因。

在阅读文章之前,建议大家特别是对 XPage 不是很熟悉的读者阅读一下文章 “XPage 全接触”,以对 XPage 有个基本的了解。


客户端 JavaScript 和 服务器端 JavaScript

 

JavaScript 在开发 XPage 程序的过程中起着至关重要的作用,在 XPage 中它分为客户端 JavaScript 和 服务器端 JavaScript。顾名思义,客户端 JavaScript 指的是 JavaScript 执行在客户端,而服务器端 JavaScript 则指的是 JavaScript 执行在服务器端。下面介绍一下两者相同点和不同点。

相同点包括:

  • 相同的关键字、运算符以及语法。
  • 对象/变量生命周期规则相同。

不同点主要包括:

  • 对于服务器端 JavaScript,浏览器相关的全局变量不再适用,例如 window,document,location。
  • 编写服务器端 JavaScript 时,可以使用平台相关的和 JSF 相关的全局变量,例如 session,database,param,context & facesContext 等等。
  • 性能不同。由于服务器端的 JavaScript 需要由客户端发送到服务器端执行并返回,因此服务器端 JavaScript 相对于客户端 JavaScript,性能比较低。
  • 使用客户端 JavaScript 验证数据有时候失效。例如恶意的数据攻击、可以在浏览器中禁止客户端的 JavaScript 等等。
  • 服务器端 JavaScript 可以直接嵌入 Java 代码。

查看日志 / 错误信息

如果你是初次接触 XPage 程序开发,那么你可能会发现:除了相对比较容易辨别的语法错误之外,如何调试其他错误,例如逻辑性错误则相当困难。到目前为止,Lotus Designer 并没有提供能够像调试客户端代码那样具有中断程序、查看各种变量值等功能的调试工具。事实上,我们可以添加一些日志信息来满足我们调试的基本功能,我们也希望不久的将来 Lotus Designer 能提供功能强大的、能够调试服务器端代码的调试工具。

你有两种方式在服务器端 JavaScript 中添加写日志的代码:

  • 第一种是常用的打印语句,例如 print(“这是日志纪录样例”),我们可以在 Lotus Notes Data 目录下文件夹 IBM_TECHNICAL_SUPPORT/console.log 中找到。
  • 第二种是使用 _dump(“这是日志纪录样例”) 来打印一个对象的详细信息(print 打印单一的字符串),日志也是存在 console.log 中。

由于上述两种方式所添加的日志都是存储在 console.log 中,而当程序中日志达到一定程度的时候,console.log 就会覆盖前面的日志。为了解决此类问题,我们可以使用第三种方法:使用代码 java.util.logging.Logger.getLogger("xpage").warning("这是日志纪录样例"); 来添加日志,我们既可以在 Lotus Notes Data 目录下文件夹 IBM_TECHNICAL_SUPPORT/console.log 中找到,也可以在当前操作系统用户的 Home 目录(如 C:/Documents and Settings/Administrator)下的 java0.log 中找到。我们需要修改文件 logging. properties(在文件夹 ../Notes/jvm/lib 中),如代码清单 1 所示。


清单 1: logging.properties

				
…
# "handlers" specifies a comma separated list of log Handler 
# classes.  These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.

handlers= java.util.logging.ConsoleHandler  //删除
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler //添加

# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers.  For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter    //删除
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter //添加

完成了上面的设置之后,重启 Lotus Designer,


XPage 常见错误分析

 

XPage 常见错误主要包括:

  • 语法错误
  • HTTP 错误
  • 其他的逻辑性错误
  • 空白页面

语法错误

如图 1 所示,它是一个仅仅包含一个按钮的 XPage 程序,但是有一个语法错误。通常这类错误都比较简单,我们可以看到编辑器中会有错误提示,在示例中它并且显示了错误的原因 “Element type "xp:button" must be followed by either attribute specifications, ">" or "/>".”


图 1. 语法错误示例
图 1. 语法错误示例

HTTP 错误

相对于语法错误,分析这类错误要相对难很多。下面列举了主要的 HTTP 错误。

  • 403 错误 - You are forbidden to perform this operation

    出现此类错误主要是因为当前 Notes ID 权限不够。你可以选择更换 Notes ID 或者 给这个 ID 授予足够的权限。

  • 404 错误 - Item Not Found Exception

    出现此类错误可能有以下两种原因:

    1. 确定你正在使用正确的网址和正确的扩展名 (.xsp)。如果你访问服务器(testserver)上 names.nsf 中 XPage demo,那么正确的网址应该为:http://testserver/names.nsf/demo.xsp。
    2. XPage 没有编译成功。此时你唯一的选择是编译此 XPage。当然也有可能是你的 XPage 中包含其他 XPage,而被包含的 XPage 有错误导致整个页面没有编译。
  • 500 错误 - Command Not Handled Exception

    出现此类错误可能有以下两种原因:

    1. XPage 中某个控件在执行的时候包含了非法的代码。一个典型的例子是服务器端执行 alert(“I am a wrong command”),由于 alert 在服务器端不是一个合法的函数,它将导致 500 错误。
    2. 如果没有出现 1 中的错误,你可以选择做一些简单的改动并保存,有时候能解决一些这类错误。

不论出现哪种 HTTP 错误,大家都可以查看 Lotus Notes Data 目录下的日志文件,查看具体的错误信息,具体可以参考如何 查看日志 / 错误信息这一节。

逻辑性错误

开发较大的 XPage 程序,我们都难免会出现这样那样的逻辑性的错误。对于这类错误,我们没有什么便捷的方法。除了分析代码之外,我们可以通过添加日志信息来确定错误所出现的环节。

空白页面

初学者经常运行 XPage 程序中得到一个空白页面,这个可能由多方面原因导致的,但最常见的原因是 XPage 中具有异常没有处理。


如何在 XPage 显示错误信息

 

本节将介绍下面两种情况下的错误管理:

  • Validator 中输入非法数据
  • Java 程序抛出的异常或者错误

我们都知道,Validator 是用来验证 XPage 控件数据的合法性,它能够发生在服务器端,也可以发生在客户端。如果验证发生在客户端,XPage 将弹出带有错误信息的对话框(参考文章 “XPage 全接触”)。下面介绍一下如何用 Display Error 控件显示服务器端错误信息(需要按照图 2 配置应用程序属性)。如代码清单 2 所示,点击按钮 submit,就会出现如图 3 所示的错误信息。


图 2. 设置服务器端验证
图 2. 设置服务器端验证

清单 2: Display Error 控件示例

				
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
    <xp:table>
        <xp:tr>
            <xp:td>                
                <xp:inputText id="inputTextValRequired01" required="true">
                    <xp:this.validators>
                        <xp:validateRequired 
                            message="This is the Required field error message, ok?">
                        </xp:validateRequired>
                    </xp:this.validators>
                </xp:inputText>
            </xp:td>
        <xp:td>Required field</xp:td>
        </xp:tr>
    </xp:table>
    
    <xp:message id="message1" for="inputTextValRequired01"></xp:message>
    <xp:br></xp:br>
    <xp:button value="submit" id="submit">
        <xp:eventHandler event="onclick" submit="true"
            refreshMode="complete">
        </xp:eventHandler></xp:button>
    <xp:br></xp:br>
</xp:view>


图 3. Display Error 控件效果图
图 3. Display Error 控件效果图

我们在实际 XPage 开发中,难免会使用一些 Java 库。那么如何查看 Java 抛出来的异常 / 堆栈呢?这些错误信息是保存在 Lotus Notes Data 目录下文件夹 IBM_TECHNICAL_SUPPORT 中的 xpages_exc_ 机器名 _ 日期 @id.log。
对于大多数人来说,查看日志信息并不是很方便。实际上 XPage 可以将错误堆栈信息打印在浏览器页面上(需要设置 Application -> Properties -> XPages,如图 4 所示)。


图 4. 设置 Display default error page
图 4. 设置 Display default error page

例如,我们把一个 XPage 的 loaded 属性设成 false,用浏览器打开,如图 5 所示。


图 5. Java 堆栈
图 5. Java 堆栈

另外,用户还可以定制错误信息,在这里我们不再赘述。


客户端 JavaScript 调试

通常情况下,我们谈到的 JavaScript 调试,都是指客户端的 JavaScript 调试。目前,已经有各种各样的开发工具供我们选择,例如 FireFox 插件 FireBug,微软的 Visual Studio 等等。甚至,如果你不想使用这些工具,你可以用添加 Alert 语句来显示变量值或者使用日志。


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、付费专栏及课程。

余额充值