首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

开发一个调试 JSP 的 Eclipse 插件(1)

开发一个调试 JSP 的 Eclipse 插件(1)

JAVA 调试框架(JPDA)简介 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
    1
    2
    3
    4
    5
    6
    -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 的基本架构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                         Components                        Debugger Interface
            
               /    |-----------------------|
              /     |     VM       |
debuggee ----(      |-----------------------|  <------- JVMDI - Java VM Debug Interface
              \     |   back-end     |
               \    |-----------------------|
               /           |
comm channel -(           |  <--------------- JDWP - Java Debug Wire Protocol
               \           |
                    |---------------------|
                    | front-end      |
                    |---------------------|  <------- JDI - Java Debug Interface
                    |      UI      |
                    |---------------------|





Eclipse作为一个基于 JAVA 的调试客户端,利用 org.eclipse.jdt.debug  Plugin 提供了JDI 的具体实现。JDI 接口主要包含下面 4 个包
1
2
3
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规范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:  
1
2
3
4
5
6
7
8
1    <HTML>   
2    <HEAD>   
3    <TITLE>Hello Example</TITLE>   
4    </HEAD>   
5    <BODY>   
6    <%@ include file="greeting.jsp" %>   
7    </BODY>   
8    </HTML>




   greeting.jsp:  
          1    Hello There!<P>              2    Goodbye on <%= new java.util.Date() %>  
JSP 编译后产生的Hello_jsp.java 如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
Hello_jsp.java:
1      package org.apache.jsp;
2
3      import javax.servlet.*;
4      import javax.servlet.http.*;
5      import javax.servlet.jsp.*;
6      
7   public final class Hello_jsp extends org.apache.jasper.runtime.HttpJspBase
8          implements org.apache.jasper.runtime.JspSourceDependent {
9      
10        private static java.util.Vector _jspx_dependants;
11      
12        static {
13          _jspx_dependants = new java.util.Vector(1);
14          _jspx_dependants.add("/greeting.jsp");
15        }
16      
17        public java.util.List getDependants() {
18          return _jspx_dependants;
19        }
20      
21  public void _jspService(HttpServletRequest request,
                                   HttpServletResponse response)
22              throws java.io.IOException, ServletException {
23      
24          JspFactory _jspxFactory = null;
25          PageContext pageContext = null;
26          HttpSession session = null;
27          ServletContext application = null;
28          ServletConfig config = null;
29          JspWriter out = null;
30          Object page = this;
31          JspWriter _jspx_out = null;
32      
33      
34          try {
35            _jspxFactory = JspFactory.getDefaultFactory();
36            response.setContentType("text/html");
37            pageContext = _jspxFactory.getPageContext(this, request, response,
38                  null, true, 8192, true);
39            application = pageContext.getServletContext();
40            config = pageContext.getServletConfig();
41            session = pageContext.getSession();
42            out = pageContext.getOut();
43            _jspx_out = out;
44      
45            out.write("<HTML>    \r\n");
46            out.write("<HEAD>    \r\n");
47            out.write("<TITLE>Hello Example");
48            out.write("</TITLE>    \r\n");
49            out.write("</HEAD>    \r\n");
50            out.write("<BODY>    \r\n");
51            out.write("Hello There!");
52            out.write("<P>    \r\nGoodbye on ");
53            out.write(String.valueOf( new java.util.Date() ));
54            out.write("  \r\n");
55            out.write("    \r\n");
56            out.write("</BODY>    \r\n");
57            out.write("</HTML>  \r\n");
58          } catch (Throwable t) {
59            if (!(t instanceof javax.servlet.jsp.SkipPageException)){
60              out = _jspx_out;
61              if (out != null && out.getBufferSize() != 0)
62                out.clearBuffer();
63              if (pageContext != null) pageContext.handlePageException(t);
64            }
65          } finally {
66  if (_jspxFactory != null) _jspxFactory.releasePageContext ( pageContext);
67          }
68        }
69      }




Tomcat 又将这个 JAVA 代码编译为 Hello_jsp.class,他们位于:$Tomcat_install_path$\work\Standalone\localhost\_ 目录下。但是 JSP 文件名/行号和 JAVA 行号的对应表(以下简称SMAP) 在哪里呢?答案是,它保存在 Class 中。如果用 UltraEdit 打开这个 Class 文件,就可以找到 SourceDebugExtension 属性,这个属性用来保存 SMAP。



返回列表