站内搜索: 请输入搜索关键词

当前页面: 开发资料首页JSP 专题开发一个调试JSP的Eclipse插件

开发一个调试JSP的Eclipse插件

摘要: 开发一个调试JSP的Eclipse插件


  本文通过开发一个JSP 编辑器插件的示例,介绍了 Eclipse 中设置 JSP 断点的方法,以及如何远程调试 JSP。作为基础知识,本文的前两部分描述了 JAVA Debug 和 JSR-45 的基本原理。

  环境要求: 本文的代码是在 Eclipse3.0.0,JDK1.4.2 和 Tomcat5.0.5 上测试过的。

  JAVA 调试框架(JPDA)简介

  JPDA 是一个多层的调试框架,包括 JVMDI、JDWP、JDI 三个层次。JAVA 虚拟机提供了 JPDA 的实现。其开发工具作为调试客户端,可以方便的与虚拟机通讯,进行调试。Eclipse 正是利用 JPDA 调试 JAVA 应用,事实上,所有 JAVA 开发工具都是这样做的。SUN JDK 还带了一个比较简单的调试工具以及示例。

  下图为 JPDA 的基本架构:


                          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      |

                     |---------------------|

             

  参见: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 中与断点相关的接口。
  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    <head>    

          3    Hello Example    

          4    </head>    

          5    <body>    

          6    <%@ include file="greeting.jsp" %>    

          7    </body>    

          8      

          

  greeting.jsp:

1 Hello There!

2 Goodbye on <%= new java.util.Date() %>

  JSP 编译后产生的Hello_jsp.java 如下:

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("    \r\n");

46            out.write("<head>    \r\n");

47            out.write("Hello Example");

48            out.write("    \r\n");

49            out.write("</head>    \r\n");

50            out.write("<body>    \r\n");

51            out.write("Hello There!");

52            out.write("

\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(" \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。

  JVM 规范定义了 ClassFile 中可以包含 SourceDebugExtension 属性,保存 SMAP:

SourceDebugExtension_attribute {

  u2 attribute_name_index;

  u4 attribute_length;

  u1 debug_extension[attribute_length];

}


  我用 javassist 做了一个测试(javassist可是一个好东东,它可以动态改变Class的结构,JBOSS 的 AOP就利用了javassist,这里我们只使用它读取ClassFile的属性)


public static void main(String[] args) throws Exception{

   String[]files = {

   "E:\\Tomcat5_0_5\\work\\Catalina\\localhost\\_\\org\\apache\\jsp\\Hello_jsp.class",

   };

                

 for(int k = 0; k < files.length; k++){

    String file = files[k];

    System.out.println("Class : " + file);

    ClassFile classFile = new ClassFile(new DataInputStream(new FileInputStream(file)));

           

    AttributeInfo attributeInfo = classFile.getAttribute("SourceDebugExtension");

    System.out.println("attribute name :" + attributeInfo.getName() + "]\n\n");

    byte[]bytes = attributeInfo.get();

    String str = new String(bytes);

    System.out.println(str);   

   }

}


  这段代码显示了SourceDebugExtension 属性,你可以看到SMAP 的内容。编译JSP后,SMAP 就被写入 Class 中, 你也可以利用 javassist 修改 ClassFile 的属性。

  下面就是 Hello_jsp.class 中保存的 SMAP 内容:

SMAP

E:\Tomcat5_0_5\work\Catalina\localhost\_\org\apache\jsp\Hello_jsp.java

JSP

*S JSP

*F

+ 0 Hello.jsp

/Hello.jsp

+ 1 greeting.jsp

/greeting.jsp

*L

1:45

2:46

3:47

3:48

4:49

5:50

1#1:51

1:52

2:53

7#0:56

8:57

*E

  首先注明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具体的说明:


1:45  2:46  3:47  3:48  4:49  5:50(没有源文件代号,默认为Hello.jsp)

                   开始行号   结束行号

Hello.jsp:    1 ->  Hello_jsp.java:    45

              2 ->                     46

              3 ->                     47           48

              4 ->                     49

              5 ->                     50

1#1:51  1:52  2:53(1#1表示 greeting.jsp 的第1行)

greeting.jsp:    1 ->  Hello_jsp.java:       51           52

                 2 ->                     53 

        

7#0:56  8:57(7#0表示 Hello.jsp 的第7行)

Hello.jsp:     7 ->  Hello_jsp.java:       56

               8 ->                     57

  开发一个JSP编辑器

  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就打开了。

  在JSP编辑器中设置断点

  在编辑器中添加断点的操作方式有两种,一种是在编辑器左侧垂直标尺上双击,另一种是在左侧垂直标尺上点击鼠标右键,选择菜单"添加/删除断点"。

  在 Eclipse 的实现中,添加断点实际上就是为 IFile 添加一个marker ,类型是IBreakpoint.BREAKPOINT_MARKER,然后将断点注册到 BreakpointManager。

  BreakpointManager 将产生一个 BreakpointRequest,通知正在运行的JVM Target,如果此时还没有启动 JVM,会在 JVM 启动的时候,将所有断点一起通知 JVM Target。

  添加断点使用一个 AbstractRulerActionDelegate,重载 createAction 方法,返回一个 IAction ManageBreakpointRulerAction动作:

public class ManageBreakpointRulerActionDelegate extends AbstractRulerActionDelegate{

 protected IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) {

  return new ManageBreakpointRulerAction(rulerInfo, editor);

 }

}


  为了将 ManageBreakpointRulerActionDelegate 添加到文本编辑器左侧标尺的鼠标右键菜单,并且能够处理左侧标尺的鼠标双击事件,在 plugin.xml 中加入定义。

  处理双击事件:



 

   

   

 



       

  添加右键菜单:



 

     

    

 




  ManageBreakpointRulerAction 是实际添加断点的Action,实现了 IUpdate 接口,这个Action的工作,就是判断当前选中行是否存在断点类型的 Marker,如果不存在创建一个,如果存在,将它删除。

public class ManageBreakpointRulerAction extends Action implements IUpdate{

     

     private IVerticalRulerInfo rulerInfo;

     private ITextEditor textEditor;

    

     private String BPmarkerType ;     //当点Marker的类型

     private List allMarkers;       //当前鼠标点击行所有的Marker

     private String addBP;   //Action 的显示名称

    

public ManageBreakpointRulerAction(IVerticalRulerInfo ruler, ITextEditor editor){

     this.rulerInfo = ruler;

     this.textEditor = editor;

     BPmarkerType = IBreakpoint.BREAKPOINT_MARKER;

     addBP = "添加/删除断点"; //$NON-NLS-1$

     setText(this.addBP);

}

    

public void update() {

  this.allMarkers = this.fetchBPMarkerList(); 

}

    

public void run(){

 if(this.allMarkers.isEmpty())

   this.addMarker();

 else

   this.removeMarkers(this.allMarkers);

}

}

  

  update 方法会在点击时首先调用,这时就可以收集当前选中行是否有marker了(调用fetchBPMarkerList方法),如果有,就保存在 变量allMarkers 中。由于ManageBreakpointRulerAction每一次都产生一个新的实例,因此不会产生冲突。

  下面是update的调用栈,可以看出,update方法是在鼠标点击事件中被调用的:


ManageBreakpointRulerAction.update() line: 55

ManageBreakpointRulerActionDelegate(AbstractRulerActionDelegate).update() line: 114

ManageBreakpointRulerActionDelegate(AbstractRulerActionDelegate).mouseDown(MouseEvent) line: 139


  updae被调用后,会执行 run 方法,就可以根据 allMarkers.isEmpty() 确定要删除还是添加 marker 了。

  添加断点的时候,首先利用 IVerticalRulerInfo,获取鼠标点击的行号,根据行号,从 Document 模型中取得该行的描述IRegion,得到开始字符位置和结束字符位置,创建一个 JSP 断点。


  protected void addMarker() {

  IEditorInput editorInput= this.getTextEditor().getEditorInput();

  

  IDocument document= this.getDocument();

  //the line number of the last mouse button activity

  int rulerLine= this.getRulerInfo().getLineOfLastMouseButtonActivity();

  try{

   int lineNum = rulerLine + 1;

   if(lineNum > 0){

       //Returns a description of the specified line

    IRegion iregion = document.getLineInformation(lineNum - 1);

    int charStart = iregion.getOffset();

    int charEnd = (charStart + iregion.getLength()) - 1;

    JSPDebugUtility.createJspLineBreakpoint(this.getResource(), 

             lineNum, charStart, charEnd);

   }

  }catch(CoreException coreexception){

   coreexception.printStackTrace();

  }

  catch(BadLocationException badlocationexception){

   badlocationexception.printStackTrace();

  }  

 }

 

  注册 JSP 断点为支持 JSR-45 规范,Eclipse 中提供了 JavaStratumLineBreakpoint。不过它目前是一个 internal 的实现,在以后的版本中不能保证不作修改。这里为了简单起见,直接从 JavaStratumLineBreakpoint 继承。


        public class JSPBreakpoint extends JavaStratumLineBreakpoint {

        public JSPBreakpoint(IResource resource, String stratum, String sourceName,

                String sourcePath, String classNamePattern, int lineNumber,

                int charStart, int charEnd, int hitCount, boolean register,

                Map attributes) throws DebugException {

            super(resource, stratum, sourceName, sourcePath, classNamePattern,

                    lineNumber, charStart, charEnd, hitCount, register, attributes);

        }

    }


  查看 JavaStratumLineBreakpoint 的源代码可以知道,创建 JavaStratumLineBreakpoint 的时候做了两件事情:

  (1) 创建断点类型的 marker, 并且设置了marker的属性resource.createMarker(markerType);

  (2) 将断点注册到断点管理器

  DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(this); 断点管理器负责产生一个 BreakpointRequest,通知正在运行的JVM Target 如果此时还没有启动 JVM,会在 JVM 启动的时候,将所有断点一起通知 JVM Target。

  下面是 JavaStratumLineBreakpoint 构造函数中的代码:


    IWorkspaceRunnable wr= new IWorkspaceRunnable() {

   public void run(IProgressMonitor monitor) throws CoreException {

    // create the marker

    setMarker(resource.createMarker(markerType));    

    // modify pattern

    String pattern = classNamePattern;

    if (pattern != null && pattern.length() == 0) {

     pattern = null;

    }

    // add attributes

    addLineBreakpointAttributes(attributes, getModelIdentifier(), true, 

          lineNumber, charStart, charEnd);

    addStratumPatternAndHitCount(attributes, stratum, sourceName, 

sourcePath, pattern, hitCount);

    // set attributes

    ensureMarker().setAttributes(attributes);

    

    register(register);

   }

  };

  run(null, wr);

    

    protected void register(boolean register) throws CoreException {

  if (register) {

   DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(this);

  } else {

   setRegistered(false);

  }

 }

  

  移除断点的时候,根据 marker 找到相应的 IBreakpoint,从 BreakpointManager 中移除 BreakpointManager 会自动删除 marker,通知 JVM Target。

breakpointManager  = DebugPlugin.getDefault().getBreakpointManager();

IBreakpoint breakpoint = breakpointManager.getBreakpoint(IMarker);

breakpointManager.removeBreakpoint(breakpoint, true);

        

  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


  public void addToTarget(JDIDebugTarget target) throws CoreException {

  IMarker marker = this.getMarker();

     IResource resource = marker.getResource();

     String targetString = target.getLaunch().getLaunchConfiguration().getName();

  IJSPNameUtil util = JSPDebugUtility.getJSPNameUtil(targetString);

     

  // pre-notification

  fireAdding(target);

    

  String referenceTypeName;

  try {

   referenceTypeName = getPattern();

   //如果没有设置 Pattern, 根据 Server 的类型, 产生新的 Pattern 

   if(referenceTypeName == null || 

      "".equals(referenceTypeName.trim()) ||

      "*".equals(referenceTypeName.trim())){

       referenceTypeName = util.referenceTypeName(resource);

   }

   

  } catch (CoreException e) {

   JDIDebugPlugin.log(e);

   return;

  }

  

  this.ensureMarker().setAttribute(TYPE_NAME, referenceTypeName);

  String sourcePath = util.sourcePath(resource);

  this.ensureMarker().setAttribute(JSPBreakpoint.SOURCE_PATH, sourcePath);

  

  String classPrepareTypeName= referenceTypeName;

  

  //如果这时 class 还没有被加载, 注册一个 ClassPrepareRequest 请求

  //

  //当 class 加载的时候, 首先会触发 JavaBreakpoint 的 handleClassPrepareEvent 方法

  //调用 createRequest(target, event.referenceType())  newRequest() 

  //    createLineBreakpointRequest() 创建 enable或disable 断点的请求

  //

  //  设置 enable/disable 动作在 configureRequest()  updateEnabledState(request) 方法中

  //  根据 getMarker().getAttribute(ENABLED, false) 确定断点是否有效

  

  registerRequest(target.createClassPrepareRequest(classPrepareTypeName), target);

  

  // create breakpoint requests for each class currently loaded

  VirtualMachine vm = target.getVM();

  if (vm == null) {

   target.requestFailed("Unable_to_add_breakpoint_-_VM_disconnected._1"), 

   null);   }

  List classes = null;

  try {

   classes= vm.allClasses();

  } catch (RuntimeException e) {

   target.targetRequestFailed("JavaPatternBreakpoint.0"), e); 

  }

  if (classes != null) {

   Iterator iter = classes.iterator();

   while (iter.hasNext()) {

    ReferenceType type= (ReferenceType)iter.next();

    if (installableReferenceType(type, target)) {

     createRequest(target, type);

    }

   }

  }

 } 
  调试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)


    D:\j2sdk1.4.2\bin\java  

-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8888,server=y,
suspend=n -Djava.endorsed.dirs="..\common\endorsed" -classpath "D:\j2sdk1.4.2\lib\tools.jar;..\bin\bootstrap.jar" -Dcatalina.base=".." -Dcatalina.home=".." -Djava.io.tmpdir="..\temp" org.apache.catalina.startup.Bootstrap start

  -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 的功能。

</td> </tr> <tr> <td vAlign=top align=left height="100%">
↑返回目录
前一篇: Eclipse插件开发之FindBugs插件
后一篇: Win2000 Server IIS+Tomcat5多站点配置