首页产品库评测行情新闻|手机数码笔记本台式机DIY硬件数字家庭数码相机办公外设|软件下载游戏开发|社区

更多

数码相机
MP4
LCD
机箱
音箱

软件资讯设计 工具 系统 开发 安全 办公 陶吧 IT教育 Vista频道 | 下载中心酷我音乐盒 腾讯QQ
天极网 > 开发频道>开发一个调试JSP的Eclipse插件

开发一个调试JSP的Eclipse插件

2006-02-06 09:32作者:焦烈焱出处:ibm责任编辑:方舟

  开发一个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); 
} 
} 
  

  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); 
    } 
   } 
  } 
 }     
 

关注此文的读者还看过:

返回开发频道首页

软件频道最新更新

热点推荐

IT嘉年华

编辑推荐

软件下载

热门
推荐

网友关注

软件
资料
游戏

装机推荐

文章排行

本周
本月
最新更新
天极服务|关于我们|About us|网站律师|RSS订阅|友情合作|加入我们|天极动态|网站地图|意见反馈|MSN/QQ上看天极
Copyright (C) 1999-2012 Yesky.com, All Rights Reserved 版权所有 天极网络