- 浏览: 263792 次
- 性别:
- 来自: 南京
文章分类
最新评论
-
Java_zhou:
...
Oracle自定义函数 -
wmj007:
SELECT USER_TAB_COLS.TABLE_NAME ...
Oracle 查询字段详细信息 -
avi9111:
怎么可以个人有一个MQ? 咁威的
使用c#操作IBM WebSphere MQ -
chouchouzzj:
8个小时。。。让我想起了世界时和北京时之间的差距,MQ存在时区 ...
使用c#操作IBM WebSphere MQ
本文通过开发一个JSP 编辑器插件的示例,介绍了 Eclipse 中设置 JSP 断点的方法,以及如何远程调试 JSP。作为基础知识,本文的前两部分描述了 JAVA Debug 和 JSR-45 的基本原理。<!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
环境要求: 本文的代码是在 Eclipse3.0.0,JDK1.4.2 和 Tomcat5.0.5 上测试过的。
JPDA 是一个多层的调试框架,包括 JVMDI、JDWP、JDI 三个层次。JAVA 虚拟机提供了 JPDA 的实现。其开发工具作为调试客户端,可以方便的与虚拟机通讯,进行调试。Eclipse 正是利用 JPDA 调试 JAVA 应用,事实上,所有 JAVA 开发工具都是这样做的。SUN JDK 还带了一个比较简单的调试工具以及示例。
- JVMDI 定义了虚拟机需要实现的本地接口
- JDWP 定义了JVM与调试客户端之间的通讯协议
调试客户端和JVM 既可以在同一台机器上,也可以远程调试。JDK 会包含一个默认的实现 jdwp.dll,JVM 允许灵活的使用其他协议代替 JDWP。SUN JDK 有两种方式传输通讯协议:Socket 和共享内存(后者仅仅针对 Windows),一般我们都采用 Socket 方式。
你可以用下面的参数,以调试模式启动JVM
-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n -Xrunjdwp JVM 加载 jdwp.dll transport=dt_socket 使用 Socket 传输 address 表示调试端口号 server=y 表示 JVM 作为服务器,建立 Socket suspend=n 表示启动过程中,JVM 不会挂起去等待调试客户端连接
- JDI 则是一组JAVA接口
如果是一个 JAVA 的调试客户端,只要实现 JDI 接口,利用JDWP协议,与虚拟机通讯,就可以调用JVMDI了。
下图为 JPDA 的基本架构:
|
参见:http://java.sun.com/j2se/1.4.2/docs/guide/jpda/architecture.html
Eclipse作为一个基于 JAVA 的调试客户端,利用 org.eclipse.jdt.debug Plugin 提供了JDI 的具体实现。JDI 接口主要包含下面 4 个包
com.sun.jdi com.sun.jdi.connect com.sun.jdi.event com.sun.jdi.request
本文不对 JDI 进行深入阐述,这里重点介绍 JDI 中与断点相关的接口
- com.sun.jdi
主要是JVM(VirtualMachine) 线程(ThreadReference) 调用栈(StackFrame) 以及类型、实例的描述。利用这组接口,调试客户端可以用类似类反射的方式,得到所有类型的定义,动态调用 Class 的方法。 - com.sun.jdi.event
封装了JVM 产生的事件, JVM 正是将这些事件通知给调试客户端的。例如 BreakpointEvent 就是 JVM 执行到断点的时候,发出的事件;ClassPrepareEvent就是 Class 被加载时发出的事件。 - com.sun.jdi.request
封装了调试客户端可以向 JVM发起的请求。例如 BreakpointRequest 向 JVM 发起一个添加断点的请求;ClassPrepareRequest 向 JVM 注册一个类加载请求,JVM 在加载指定 Class 的时候,就会发出一个 ClassPrepareEvent 事件。
|
|
JSR-45(Debugging Support for Other Languages)为那些非 JAVA 语言写成,却需要编译成 JAVA 代码,运行在 JVM 中的程序,提供了一个进行调试的标准机制。也许字面的意思有点不好理解,什么算是非 JAVA 语言呢?其实 JSP 就是一个再好不过的例子,JSR-45 的样例就是一个 JSP。
JSP的调试一直依赖于具体应用服务器的实现,没有一个统一的模式,JSR-45 针对这种情况,提供了一个标准的模式。我们知道,JAVA 的调试中,主要根据行号作为标志,进行定位。但是 JSP 被编译为 JAVA 代码之后,JAVA 行号与 JSP 行号无法一一对应,怎样解决呢?
JSR-45 是这样规定的:JSP 被编译成 JAVA 代码时,同时生成一份 JSP 文件名和行号与 JAVA 行号之间的对应表(SMAP)。JVM 在接受到调试客户端请求后,可以根据这个对应表(SMAP),从 JSP 的行号转换到 JAVA 代码的行号;JVM 发出事件通知前, 也根据对应表(SMAP)进行转化,直接将 JSP 的文件名和行号通知调试客户端。
我们用 Tomcat 5.0 做个测试,有两个 JSP,Hello.jsp 和 greeting.jsp,前者 include 后者。Tomcat会将他们编译成 JAVA 代码(Hello_jsp.java),JAVA Class(Hello_jsp.class) 以及 JSP 文件名/行号和 JAVA 行号之间的对应表(SMAP)。
Hello.jsp:
|
greeting.jsp:
1 Hello There!<P> 2 Goodbye on <%= new java.util.Date() %>
JSP 编译后产生的Hello_jsp.java 如下:
|
Tomcat 又将这个 JAVA 代码编译为 Hello_jsp.class,他们位于: $Tomcat_install_path$\work\Standalone\localhost\_ 目录下。但是 JSP 文件名/行号和 JAVA 行号的对应表(以下简称SMAP) 在哪里呢?答案是,它保存在 Class 中。如果用 UltraEdit 打开这个 Class 文件,就可以找到 SourceDebugExtension 属性,这个属性用来保存 SMAP。
JVM 规范定义了 ClassFile 中可以包含 SourceDebugExtension 属性,保存 SMAP:
|
我用 javassist 做了一个测试(javassist可是一个好东东,它可以动态改变Class的结构,JBOSS 的 AOP就利用了javassist,这里我们只使用它读取ClassFile的属性)
|
这段代码显示了SourceDebugExtension 属性,你可以看到SMAP 的内容。编译JSP后,SMAP 就被写入 Class 中, 你也可以利用 javassist 修改 ClassFile 的属性。
下面就是 Hello_jsp.class 中保存的 SMAP 内容:
|
首先注明JAVA代码的名称:Hello_jsp.java,然后是 stratum 名称: JSP。随后是两个JSP文件的名称 :Hello.jsp、greeting.jsp。两个JSP文件共10行,产生的Hello_jsp共69行代码。最后也是最重要的内容就是源文件文件名/行号和目标文件行号的对应关系(*L 与 *E之间的部分)
在规范定义了这样的格式:
源文件行号 # 源文件代号,重复次数 : 目标文件开始行号,目标文件行号每次增加的数量
(InputStartLine # LineFileID , RepeatCount : OutputStartLine , OutputLineIncrement)
源文件行号(InputStartLine) 目标文件开始行号(OutputStartLine) 是必须的。下面是对这个SMAP具体的说明:
|
|
Eclipse 提供了 TextEditor,作为文本编辑器的父类。由于 Editor 的开发不是本文的重点,不做具体论述。我们可以利用 Eclipse 的 Plugin 项目向导,生成一个简单的 JSP 编辑器:
(1)点击 File 菜单,New -> Project -> Plug-in Project ;
(2)输入项目名称 JSP_DEBUG,下一步;
(3)输入 plugin ID : com.jsp.debug
Plugin Class name : com.jsp.debug.JSP_DebugPlugin
(4)选择用模板创建
使用 Plug-in with editor,输入
Java Package Name :com.jsp.editors
Editor Class Name :JSPEditor
File extension :jsp
一个 jsp editor 就产生了。
运行这个Plugin,新建一个JAVA项目,新建一个 Hello.jsp 和 greeting.jsp,在 Navigator 视图双击 jsp,这个editor就打开了。
|
|
在编辑器中添加断点的操作方式有两种,一种是在编辑器左侧垂直标尺上双击,另一种是在左侧垂直标尺上点击鼠标右键,选择菜单"添加/删除断点"。
在 Eclipse 的实现中,添加断点实际上就是为 IFile 添加一个marker ,类型是IBreakpoint.BREAKPOINT_MARKER,然后将断点注册到 BreakpointManager。
BreakpointManager 将产生一个 BreakpointRequest,通知正在运行的JVM Target,如果此时还没有启动 JVM,会在 JVM 启动的时候,将所有断点一起通知 JVM Target。
添加断点使用一个 AbstractRulerActionDelegate,重载 createAction 方法,返回一个 IAction ManageBreakpointRulerAction动作:
|
为了将 ManageBreakpointRulerActionDelegate 添加到文本编辑器左侧标尺的鼠标右键菜单,并且能够处理左侧标尺的鼠标双击事件,在 plugin.xml 中加入定义。
处理双击事件:
|
添加右键菜单:
|
ManageBreakpointRulerAction 是实际添加断点的Action,实现了 IUpdate 接口,这个Action的工作,就是判断当前选中行是否存在断点类型的 Marker,如果不存在创建一个,如果存在,将它删除。
|
update 方法会在点击时首先调用,这时就可以收集当前选中行是否有marker了(调用fetchBPMarkerList方法),如果有,就保存在 变量allMarkers 中。由于ManageBreakpointRulerAction每一次都产生一个新的实例,因此不会产生冲突。
下面是update的调用栈,可以看出,update方法是在鼠标点击事件中被调用的:
|
updae被调用后,会执行 run 方法,就可以根据 allMarkers.isEmpty() 确定要删除还是添加 marker 了。
添加断点的时候,首先利用 IVerticalRulerInfo,获取鼠标点击的行号,根据行号,从 Document 模型中取得该行的描述IRegion,得到开始字符位置和结束字符位置,创建一个 JSP 断点。
|
注册 JSP 断点为支持 JSR-45 规范,Eclipse 中提供了 JavaStratumLineBreakpoint。不过它目前是一个 internal 的实现,在以后的版本中不能保证不作修改。这里为了简单起见,直接从 JavaStratumLineBreakpoint 继承。
|
查看 JavaStratumLineBreakpoint 的源代码可以知道,创建 JavaStratumLineBreakpoint 的时候做了两件事情:
(1) 创建断点类型的 marker, 并且设置了marker的属性
resource.createMarker(markerType);
(2) 将断点注册到断点管理器
DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(this); 断点管理器负责产生一个 BreakpointRequest,通知正在运行的JVM Target 如果此时还没有启动 JVM,会在 JVM 启动的时候,将所有断点一起通知 JVM Target。
下面是 JavaStratumLineBreakpoint 构造函数中的代码:
|
移除断点的时候,根据 marker 找到相应的 IBreakpoint,从 BreakpointManager 中移除 BreakpointManager 会自动删除 marker,通知 JVM Target。
|
JSPBreakpoint 重载了父类的addToTarget(JDIDebugTarget target) 方法。重载这个方法的目的是根据不同的应用服务器,设置不同的 referenceTypeName和sourcePath。我们知道,每种应用服务器编译 JSP 产生Java Class 名称的规则都不相同,例如Tomcat编译Hello.jsp 产生的Java 类名为 org.apache.jsp. Hello_jsp,而WebSphere6.0 却是 com.ibm._jsp._Hello。只有确定服务器类型,才能知道referenceTypeName 和souecePath应该是什么。目前通过启动 JVM 时target 名称来判断应用服务器类型: String targetString = target.getLaunch().getLaunchConfiguration().getName(); 如果targetString 包含 Tomcat ,就认为是 Tomcat。
产生 referenceTypeName 后首先创建一个 ClassPrepareRequest 通知,然后从vm中取出所有的classes,如果是当前的 Class,再创建一个添加断点通知。之所以这样做,是因为有可能这个 Class 还没有被 JVM 加载,直接通知 JVM 没有任何意义。在 Class 被加载的时候,JVM 会通知 Eclipse,这个时候,才产生添加断点通知。需要指出的是,本文示例代码获取 referenceTypeName 的方法不是很完善:
(1) 仅仅实现了Tomcat 读者有兴趣可以实现更多的Web容器,例如 JBoss3 以上,WebSphere6.0
(2) 一些特殊情况没有处理例如 路径名为package的jsp,路径名或文件名带有数字的jsp
|
|
现在我们可以调试 JSP 了。
(1)运行 JSP_DEBUG plugin
首先在 run -> run 中添加一个 Run-time Workbench,点击 run 按钮,Eclipse 的Plugin开发环境会启动一个新的Eclipse,这个新启动的 Eclipse 中,我们创建的 JSP_DEBUG plugin 就可以使用了。新建 一个 JAVA 项目 Test (注意,一定要是JAVA项目),新建一个 Hello.jsp 和 greeting.jsp,打开Hello.jsp,在编辑器左侧标尺双击,就出现了一个断点。
(2)以 Debug 模式启动Tomcat:
windows 开始 -> 运行,键入 cmd,启动一个命令行窗口:
cd E:\Tomcat5_0_5\bin
(我的 Tomcat 安装在 E:\Tomcat5_0_5 目录,JDK 安装在 D:\j2sdk1.4.2)
|
-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=n 表示以调试方式启动,端口号是 8888 classpath中要加入 D:\j2sdk1.4.2\lib\tools.jar,因为我是 Tomcat5.0.5,如果是5.5就不需要了。
(3) 测试Hello.jsp
将 Hello.jsp 和 greeting.jsp 拷贝到 E:\Tomcat5_0_5\webapps\ROOT 目录,从浏览器访问 Hello.jsp http://localhost:8000/Hello.jsp。成功的话就可以继续下面的工作了,如果失败,检查你的Tomcat设置。
(4)启动远程调试
在 Eclipse 中启动远程调试,将 Eclipse 作为一个 Debug 客户端,连接到 Tomcat 。在 Java 透视图中,点击 Run -> Debug ,添加一个 Remote Java Application,名称是 Start Tomcat Server(不能错,因为我们要根据这个名称,判断当前的 Web Server 类型)
project是创建的 Test 项目
Port 为 8888,和启动 Tomcat 时设置的一样
点击 Debug 按钮,就可以连接到 Tomcat 上了。切换到 Debug 透视图,在Debug 视图中,能够看到所有 Tomcat 中线程的列表。
(5)调试Hello.jsp
为 Hello.jsp 添加断点,然后从浏览器访问Hello.jsp,就可以在断点处挂起了。你可以使用单步执行,也可以在Variables视图查看jsp中的变量信息。
由于 Eclipse 自身的实现,现在的 JSP Editor 有一个问题,单步执行到 include jsp 行后,会从Hello.jsp的1行再次执行。这是因为 Eclipse JDT Debug视图缓存了 StackFrame 中已经打开的Editor,StackFrame不改变时,不会再重新计算当前调试的是否是其他Resource。本来应该打开 greeting.jsp的,现在却从 Hello.jsp 的第 1 行开始执行了。
|
|
很多集成开发环境都支持 JSP 的调试,在 Eclipse 中也有 MyEclipse 这样的插件完成类似的功能。但是在 JSR-45 规范产生前,每种应用服务器对 JSP Debug 的实现是不一样的,例如 WebSphere 5 就是在 JSP 编译产生的 JAVA 代码中加入了两个数组,表示源文件和行号的对应信息。Tomcat 率先实现了 JSR-45 规范,WebSphere 6.0 现在也采取这种模式, 有兴趣的话,可以查看 WebSphere 6.0 编译的 Class,和 Tomcat 不一样,SMAP 文件会和java代码同时产生。
但是启动server前,需要设置 JVM 参数 was.debug.mode = true
同时在 ibm-web-ext.xmi 中设置
|
利用本文的基本原理,我们也可以开发其他基于 JAVA 脚本语言的编辑器(例如 Groovy),为这个编译器加入 Debug 的功能。
debug.zip | 260 KB | HTTP |
jsp_debug_project.zip | 280 KB | HTTP |
关于下载方法的信息 | Get Adobe® Reader® |
|
- JPDA
http://java.sun.com/j2se/1.4.2/docs/guide/jpda/
- JSR-45
http://www.jcp.org/en/jsr/detail?id=45
- IBM 专家 Scott Johnson 的文章
WebSphere Application Server V6 中的 JavaServer Pages
http://www-128.ibm.com/developerworks/cn/websphere/techjournal/0412_johnson/0412_johnson.html
|
|
焦烈焱,上海普元软件公司程序经理,通过 jiaolieyan@hotmail.com 可以和他联系。 |
相关推荐
本文通过开发一个jsp 编辑器插件的示例,介绍了Eclipse 中设置JSP 断点的方法, 以及如何远程调试JSP。作为基础知识,本文的前两部分描述了Java Debug 和JSR-45 的 基本原理。
推荐一个Eclipse的插件来协助J2EE开发,这个插件名 称nqLomboz。Lomboz插件不仅仅可用于支持JSP语法 检查和调试,它更主要的作用是能够将很多Java应用服 务器(~[1Tomcat、JBOss、WebLogic等)、J2EE组件 和Web应用...
8.3 基于数据库系统的Eclipse插件开发 288 案例8-3 Eclipse插件中数据库的应用 288 第二部分 项目实践 第9章 Shopping Online网站概述及设计方案 296 9.1 Shopping Online购物在线网站总体描述 296 9.1.1...
些版本为 phpeclipse_1.1.8; PHPeclipse这个插件包括的...安装Apache,Tomcat及php、Mysql和Eclipse插件(Eclipse + MyEclipse + Phpeclipse) 官方网址:http://www.phpeclipse.de/,http://www.phpeclipse.net/
eclipse 连接Tomcat 插件. Tomcat 是一个轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。
第八章:介绍eclipse插件,以及插件开发的环境,你将学习怎样建立简单的插件,发布插件以使别人可以使用 第九章:更多的插件代码显示如何建立定制编辑器,视图,参数页面 附录 附录A:eclipse的java相关菜单快速...
ssm 体检预约管理系统是一套完善的信息系统,结合springMVC框架完成本系统,对理解JSP java编程开发语言有帮助系统采用SSM框架(MVC模式开发),系统具有完整的源代码和数据库, 系统主要采用B/S模式开发。 前段...
ssm 设备采购管理系统是一套完善的信息系统,结合springMVC框架完成本系统,对理解JSP java编程开发语言有帮助系统采用SSM框架(MVC模式开发),系统具有完整的源代码和数据库, 系统主要采用B/S模式开发。 前段...
ssm 房屋销售管理系统是一套完善的信息系统,结合springMVC框架完成本系统,对理解JSP java编程开发语言有帮助系统采用SSM框架(MVC模式开发),系统具有完整的源代码和数据库, 系统主要采用B/S模式开发。系统...
ssm 科研奖励申报管理系统是一套完善的信息系统,结合springMVC框架完成本系统,对理解JSP java编程开发语言有帮助系统采用SSM框架(MVC模式开发),系统具有完整的源代码和数据 库,系统主要采用B/S模式开发。...
ssm 网上订餐管理系统是一套完善的信息系统,结合springMVC框架完成本系统,对理解JSP java编程开发语言有帮助系统采用SSM框架(MVC模式开发),系统具有完整的源代码和数据库, 系统主要采用B/S模式开发。系统...
Lomboz是Eclipse的一个主要的开源插件(open-source plug-in),Lomboz插件能够使Java开发者更好的使用Eclipse去创建,调试和部署一个100%基于J2EE的Java应用服务器。 Lomboz插件的使用,使得Eclipse将多种J2EE的...
基于jsp+servelet+javaweb的网上书店,java项目。 eclipse和idea都能打开运行。 推荐环境配置:eclipse/idea jdk1.8 maven mysql 前端技术:jsp,Ajax,Json 后端技术:servlet,javaweb 本系统共分为两个角色:管理...
基于Spring、SpringMVC、JSP的学科竞赛管理系统,java项目。 eclipse和idea都能打开运行。 推荐环境配置:eclipse/idea jdk1.8 maven mysql 前端技术:jsp,Ajax,Json 后端技术:Spring、JDBC 本系统共分为两个角色...
MyEclipse 是一个十分优秀的用于开发Java, J2EE的 Eclipse 插件集合,MyEclipse的功能非常强大,支持也十分广泛,尤其是对各种开源产品的支持十分不错。MyEclipse可以支持Java Servlet,AJAX,JSP,JSF,Struts,...
MyEclipse 是一个十分优秀的用于开发Java, J2EE的 Eclipse 插件集合,MyEclipse的功能非常强大,支持也十分广泛,尤其是对各种开源产品的支持十分不错。MyEclipse可以支持Java Servlet,AJAX,JSP,JSF,Struts,...
MyEclipse 是一个十分优秀的用于开发Java, J2EE的 Eclipse 插件集合,MyEclipse的功能非常强大,支持也十分广泛,尤其是对各种开源产品的支持十分不错。MyEclipse可以支持Java Servlet,AJAX,JSP,JSF,Struts,...
前端技术:jsp,Ajax,Json 后端技术:JavaWeb,JDBC 本系统共分为两个角色:管理员和用户。 管理员:admin 123456a 用户: ww 123456 主要功能有: 借阅管理、书籍管理、出版社管理、图书种类饼状统计、用户管理、...
关于resin3.1.6版本在...很正确,我调试成功了,我也是累的要死找这个东西。可网上说得我头都大了,都是抄来抄去。 如果你是resin新手,看看吧!为新手提供点方便,资源分我就不要了,好心给我点就行了,我也很穷的。
前端技术:jsp,Ajax,Json 后端技术:JDBC,JavaWeb 本系统共分为两个角色:管理员和用户。 主要功能有: 前台: 首页展示、产品中心、排行榜、品牌库、资讯、测评。 后台: 账号管理、订单管理、商品管理、账号充值...