<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Coundy

       漫步風中,傾聽自己的腳步,在自我沉浸中,找尋逝去的靈魂

    posts - 27,comments - 2,trackbacks - 0

    【摘 要】本文通過開發一個JSP 編輯器插件的示例,介紹了 Eclipse 中設置 JSP 斷點的方法,以及如何遠程調試 JSP。作為基礎知識,本文的前兩部分描述了 JAVA Debug 和 JSR-45 的基本原理。 

      本文通過開發一個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 還帶了一個比較簡單的調試工具以及示例。
    • 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 的基本架構:

     
                              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 中與斷點相關的接口。
    • 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    <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 如下:

    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。

      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 中加入定義。

      處理雙擊事件:

    <extension  point="org.eclipse.ui.editorActions"> 
     <editorContribution 
          targetID="com.jiaoly.editors.JSPEditor" 
          id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"> 
       <action 
             label="添加/刪除斷點" 
             class="com.jiaoly.debug.ManageBreakpointRulerActionDelegate" 
             actionID="RulerDoubleClick" 
             id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"> 
       </action> 
     </editorContribution> 
    </extension> 
           

      添加右鍵菜單:

    <extension point="org.eclipse.ui.popupMenus"> 
     <viewerContribution 
         targetID="#TextRulerContext" 
         id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"> 
         <action 
           label="添加/刪除斷點" 
           class="com.jiaoly.debug.ManageBreakpointRulerActionDelegate" 
           menubarPath="addition" 
           id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"> 
        </action> 
     </viewerContribution> 
    </extension> 
    

      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); 
    } 
    } 
    
    posted on 2007-05-15 15:50 Coundy 閱讀(315) 評論(0)  編輯  收藏 所屬分類: Java

    只有注冊用戶登錄后才能發表評論。


    網站導航:
    博客園   IT新聞   Chat2DB   C++博客   博問  
     
    主站蜘蛛池模板: 涩涩色中文综合亚洲| 亚洲伊人久久大香线蕉啊| 香蕉国产在线观看免费| 四虎在线播放免费永久视频| 亚洲AV日韩AV一区二区三曲| 德国女人一级毛片免费| 亚洲AV无码一区二区三区久久精品| 成人午夜性A级毛片免费| 亚洲av日韩专区在线观看| 波多野结衣免费视频观看| 亚洲精品偷拍视频免费观看| 中文字幕精品亚洲无线码二区| 国内精品一级毛片免费看| 亚洲一级二级三级不卡| 最近高清中文字幕无吗免费看| 亚洲色欲色欲www在线播放 | 特级无码毛片免费视频尤物 | 久久电影网午夜鲁丝片免费| 亚洲色偷偷色噜噜狠狠99| 亚洲国产专区一区| 免费成人在线视频观看| 亚洲国产日韩在线人成下载| 午夜小视频免费观看| 一个人晚上在线观看的免费视频| 日本亚洲视频在线| 老司机在线免费视频| 国产在亚洲线视频观看| 亚洲AV无码久久精品狠狠爱浪潮| 国产成人免费高清激情明星 | 中文有码亚洲制服av片| 亚洲国产精品嫩草影院久久| 污污网站18禁在线永久免费观看| 亚洲私人无码综合久久网| 中文字幕亚洲一区二区三区| 久久精品国产免费观看| 高潮内射免费看片| 精品日韩亚洲AV无码| xvideos亚洲永久网址| 1000部无遮挡拍拍拍免费视频观看| 亚洲精品无码久久久久YW| 亚洲免费观看视频|