入侵Jasper以获取JSP页面的对象模型

为了对我的JSP进行一些检查和统计分析,我需要一个包含在其中的元素的类似于DOM的层次模型。 但是,解析JSP页面并不是一件容易的事,最好留给它一个出色的工具-Tomcat,Jetty,GlassFish以及其他所有工具都可以使用Jasper JSP编译器。

有一种简单的方法可以调整它以生成所需的任何输出,以将JSP转换为所需的任何形式,包括页面的对象模型:

  1. 定义一个Node.Visitor子类来处理JSP的节点(标签等)
  2. 编写一个简单的Compiler子类,重写其generateJava()来调用访问者
  3. 继承编译器执行器JspC的子类,重写其方法getCompilerClassName()以返回您的编译器的类

让我们看一下代码。

实作

1.自定义访问者

编译器将调用Visitor来处理已解析的JSP的树对象模型。 此实现仅打印有关页面中有趣的节点子集的信息,以使其嵌套清晰。

package org.apache.jasper.compiler;import java.util.LinkedList;
import org.apache.jasper.JasperException;
import org.apache.jasper.compiler.Node.CustomTag;
import org.apache.jasper.compiler.Node.ELExpression;
import org.apache.jasper.compiler.Node.IncludeDirective;
import org.apache.jasper.compiler.Node.Visitor;
import org.xml.sax.Attributes;public class JsfElCheckingVisitor extends Visitor {private String indent = "";@Overridepublic void visit(ELExpression n) throws JasperException {logEntry("ELExpression", n, "EL: " + n.getEL());super.visit(n);}@Overridepublic void visit(IncludeDirective n) throws JasperException {logEntry("IncludeDirective", n, toString(n.getAttributes()));super.visit(n);}@Overridepublic void visit(CustomTag n) throws JasperException {logEntry("CustomTag", n, "Class: " + n.getTagHandlerClass().getName() + ", attrs: "+ toString(n.getAttributes()));doVisit(n);indent += " ";visitBody(n);indent = indent.substring(0, indent.length() - 1);}private String toString(Attributes attributes) {if (attributes == null || attributes.getLength() == 0) return "";LinkedList<String> details = new LinkedList<String>();for (int i = 0; i < attributes.getLength(); i++) {details.add(attributes.getQName(i) + "=" + attributes.getValue(i));}return details.toString();}private void logEntry(String what, Node n, String details) {System.out.println(indent + n.getQName() + " at line:"+ n.getStart().getLineNumber() + ": " + details);}}

笔记:

  • 访客必须位于org.apache.jasper.compiler包中,因为基本类org.apache.jasper.compiler.Node是包私有的
  • visitBody方法触发对嵌套节点的处理
  • 还有更多我可以覆盖的方法(和通行方法doVisit),但是我只选择了对我来说有趣的那些方法
  • 节点的属性为... sax类型。 Attributes ,它包含属性名称和值作为字符串
    • attribute.getType(i)通常是CDATA
  • Node结构包含有关父节点,标签名称,标签处理程序类,源文件的相应行以及源文件的名称的信息以及其他有用信息
  • CustomTag可能是最有趣的节点类型,例如,所有JSF标签都属于这种类型

输出示例(对于JSF页面)

jsp:directive.include at line:5: [file=includes/stdjsp.jsp]
jsp:directive.include at line:6: [file=includes/ssoinclude.jsp]
f:verbatim at line:14: Class: com.sun.faces.taglib.jsf_core.VerbatimTag, attrs:
htm:div at line:62: Class: com.exadel.htmLib.tags.DivTag, attrs: [style=width:100%;]h:form at line:64: Class: com.sun.faces.taglib.html_basic.FormTag, attrs: [id=inputForm]htm:table at line:66: Class: com.exadel.htmLib.tags.TableTag, attrs: [cellpadding=0, width=100%, border=0, styleClass=clear box_main]htm:tr at line:71: Class: com.exadel.htmLib.tags.TrTag, attrs:htm:td at line:72: Class: com.exadel.htmLib.tags.TdTag, attrs:f:subview at line:73: Class: com.sun.faces.taglib.jsf_core.SubviewTag, attrs: [id=cars]jsp:directive.include at line:74: [file=/includes/cars.jsp]h:panelGroup at line:8: Class: com.sun.faces.taglib.html_basic.PanelGroupTag, attrs: [rendered=#{bookingHandler.flowersAvailable}]
...htm:tr at line:87: Class: com.exadel.htmLib.tags.TrTag, attrs: [style=height:5px]htm:td at line:87: Class: com.exadel.htmLib.tags.TdTag, attrs:

(我不打印“关闭标签”,因为很明显,当出现另一个具有相同或较小缩进的节点或输出结束时,标签结束。)

2.编译器子类

重要的部分是generateJava,我刚刚复制了它,从中删除了一些代码,并添加了对Visitor的调用。 因此,实际上下面清单中的3行是新的(6,56,70)

public class OnlyReadingJspPseudoCompiler extends Compiler {/** We're never compiling .java to .class. */@Override protected void generateClass(String[] smap) throws FileNotFoundException,JasperException, Exception {return;}/** Copied from {@link Compiler#generateJava()} and adjusted */@Override protected String[] generateJava() throws Exception {// Setup page info areapageInfo = new PageInfo(new BeanRepository(ctxt.getClassLoader(),errDispatcher), ctxt.getJspFile());// JH: Skipped processing of jsp-property-group in web.xml for the current pageif (ctxt.isTagFile()) {try {double libraryVersion = Double.parseDouble(ctxt.getTagInfo().getTagLibrary().getRequiredVersion());if (libraryVersion < 2.0) {pageInfo.setIsELIgnored("true", null, errDispatcher, true);}if (libraryVersion < 2.1) {pageInfo.setDeferredSyntaxAllowedAsLiteral("true", null,errDispatcher, true);}} catch (NumberFormatException ex) {errDispatcher.jspError(ex);}}ctxt.checkOutputDir();try {// Parse the fileParserController parserCtl = new ParserController(ctxt, this);// Pass 1 - the directivesNode.Nodes directives =parserCtl.parseDirectives(ctxt.getJspFile());Validator.validateDirectives(this, directives);// Pass 2 - the whole translation unitpageNodes = parserCtl.parse(ctxt.getJspFile());// Validate and process attributes - don't re-validate the// directives we validated in pass 1/*** JH: The code above has been copied from Compiler#generateJava() with some* omissions and with using our own Visitor.* The code that used to follow was just deleted.* Note: The JSP's name is in ctxt.getJspFile()*/pageNodes.visit(new JsfElCheckingVisitor());} finally {}return null;}/*** The parent's implementation, in our case, checks whether the target file* exists and returns true if it doesn't. However it is expensive so* we skip it by returning true directly.* @see org.apache.jasper.JspCompilationContext#getServletJavaFileName()*/@Override public boolean isOutDated(boolean checkClass) {return true;}}

笔记:

  • 我从生成Java中删除了许多对我来说不重要的代码; 对于与我预期不同的分析类型,某些代码可能会有用,因此请查看原始的Compiler类并自己决定。
  • 我不太在乎JSP EL,因此可以优化编译器,使其只需要执行一次即可。

3.编译器执行器

直接使用编译器很困难,因为它取决于许多复杂的设置和对象。 因此,最简单的方法是重用Ant任务JspC,这还有查找要处理的JSP的额外好处。 如前所述,关键是重写getCompilerClassName以返回编译器的类(第8行)

import org.apache.jasper.JspC;/** Extends JspC to use the compiler of our choice; Jasper version 6.0.29. */
public class JspCParsingToNodesOnly extends JspC {/** Overriden to return the class of ours (default = null => JdtCompiler) */@Override public String getCompilerClassName() {return OnlyReadingJspPseudoCompiler.class.getName();}public static void main(String[] args) {JspCParsingToNodesOnly jspc = new JspCParsingToNodesOnly();jspc.setUriroot("web"); // where to search for JSPs//jspc.setVerbose(1);     // 0 = false, 1 = truejspc.setJspFiles("helloJSFpage.jsp"); // leave unset to process all; comma-separatedtry {jspc.execute();} catch (JasperException e) {throw new RuntimeException(e);}}
}

笔记:

  • JspC通常会在指定的Uriroot下找到所有文件,但是您可以通过将其逗号分隔的名称传递给setJspFiles来告诉它忽略所有选定的文件。

编译依赖

以你的常春藤形式:

<dependency name="jasper" org="org.apache.tomcat" rev="6.0.29">
<dependency name="jasper-jdt" org="org.apache.tomcat" rev="6.0.29">
<dependency name="ant" org="org.apache.ant" rev="1.8.2">

执照

这里的所有代码都直接来自Jasper,因此属于同一许可证,即Apache许可证,版本2.0 。

结论

Jasper并非真正为扩展和模块化而设计,因为关键的Node类是包私有的,并且其API非常复杂,以致仅重用其中的一部分非常困难。 幸运的是,通过提供一些“伪”对象,Ant任务JspC使它可以在servlet容器之外使用,并且有一种方法可以通过很少的工作来调整它以满足我们的需求,尽管要弄清楚它并不是那么容易。 我不得不应用一些肮脏的技巧,即使用包私有类中的内容,并覆盖一个不打算被覆盖的方法( generateJava ),但是它可以工作并提供非常有价值的输出,这使得您可以做任何想做的事情用一个JSP来做。

参考:在我们的Java博客上,与我们的JCG合作伙伴 JakubHolý 合作 ,通过Jasper入侵获取JSP页面的对象模型 。
快乐的编码...
拜伦
相关文章:
  • Java Code Geeks Andygene Web原型
  • 为什么自动化测试可以提高您的开发速度
  • 代码质量对客户很重要。 很多。
  • 使用FindBugs产生更少的错误代码
  • 针对用户和新采用者的敏捷软件开发建议

翻译自: https://www.javacodegeeks.com/2011/06/hacking-jasper-to-get-object-model-of.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/374864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux自动化安装cobbler

1介绍 1.1 PXE PXE技术与RPL技术不同之处为RPL是静态路由&#xff0c;PXE是动态路由。RPL是根据网卡上的ID号加上其他记录组成的一个Frame&#xff08;帧&#xff09;向服务器发出请求。而服务器中已有这个ID数据&#xff0c;匹配成功则进行远程启动。PXE则是根据服务器端收到的…

iOS9适配系列教程

https://github.com/ChenYilong/iOS9AdaptationTips 转载于:https://www.cnblogs.com/zsw-1993/p/4879118.html

C语言形参

形参和实参区别 形参出现在函数定义中&#xff0c;在整个函数体内都可以使用&#xff0c;离开该函数则不能使用。实参出现在主调函数中&#xff0c;进入被调函数后&#xff0c;实参变量也不能使用。 形参和实参的功能是作数据传送。发生函数调用时&#xff0c;主调函数把实参…

避免延迟的JPA集合

Hibernate&#xff08;实际上是JPA&#xff09;具有集合映射&#xff1a; OneToMany&#xff0c; ManyToMany&#xff0c; ElementCollection。 所有这些默认情况下都是惰性的。 这意味着集合是List或Set接口的特定实现&#xff0c;其中包含对持久会话的引用&#xff0c;并且只…

2016年,我的和自己谈谈

2016年过去三分之一了&#xff0c;现在谈规划晚点但总比没想法强。想了半天还是从这个方面着手吧&#xff1a; 一.升级改造自己的办公学习环境&#xff1a; 给自己的电脑加内存&#xff0c;加SSD&#xff0c;再添置一个显示器&#xff0c;换上心仪已久的cherry青轴键盘&#xf…

C语言的四舍五入实现

习题3-2 高速公路超速处罚 (15 分) 按照规定&#xff0c;在高速公路上行使的机动车&#xff0c;达到或超出本车道限速的10%则处200元罚款&#xff1b;若达到或超出50%&#xff0c;就要吊销驾驶证。请编写程序根据车速和限速自动判别对该机动车的处理。 输入格式: 输入在一行中…

ACTGame项目

项目地址&#xff1a;https://github.com/alonecat06/ACTGame游戏地址&#xff1a;http://pan.baidu.com/s/1hqD3IYw 项目是一个自制单机动作游戏demo&#xff0c;方向是手游&#xff0c;使用Unity5&#xff0c;5月中开工至今。 做这个项目&#xff0c;是为加深自己对Unity的理…

Xuggler教程:帧捕获和视频创建

注意&#xff1a;这是我们的“ Xuggler开发教程 ”系列的一部分。 到目前为止&#xff0c;在我们的Xuggler教程系列中&#xff0c;我们已经对视频处理的Xuggler进行了介绍&#xff0c;并讨论了转码和媒体修改 。 在本教程中&#xff0c;我们将看到如何解码视频和捕获帧&#xf…

面向对象-原型对象

创建对象 Js中可以用构造函数模式创建对象&#xff0c;如&#xff1a; function Person(name, age, job) {this.name name;this.age age;this.job job;this.sayName function () {alert(this.name);}}var person1 new Person("Nicholas", 29, "aa");v…

索引类型

1.B树索引 在Oracle中是通用索引&#xff0c;是创建索引时的默认索引。B树索引可以是单列索引&#xff0c;也可以是组合/复合索引。B树索引最多可以包括22列。 2.位图索引 位图索引时决策支持系统&#xff08;DSS&#xff09;和数据仓库的理想选择&#xff0c;它们不应该用于事…

C语言条件运算符

先看一个error error: lvalue required as left operand of assignment| i 0 ? X 1.0 : X * x;修改后 i 0 ? (X 1.0) : (X * x);也就是说条件运算符可以执行语句&#xff0c;当是赋值语句时要加括号规定优先级&#xff0c;不然会干扰程序判断。 因为条件运算符作为三目…

EJB 3.0注入和查找简介

介绍 Enterprise JavaBeans Specification v。3.0引入了简化的&#xff0c;基于注释的API&#xff0c;用于EJB注入和查找。 EJB 3.0现在是POJO&#xff0c;可以使用简单的注释将其注入其他组件&#xff08;例如EJB和Servlet&#xff09;。 EJB 3.0是Java EE 6的许多其他基于POJ…

SignalR + MVC5 简单示例

SignalR MVC5 简单示例 原文:SignalR MVC5 简单示例本文和前一篇文章很类似&#xff0c;只不过是把 SignalR 应用在了 MVC 中 新建项目&#xff0c;选择 MVC 模板 安装 SignalR Install-Package Microsoft.AspNet.SignalR 在项目中添加文件夹 Hubs 在 Hubs 文件夹中添加 Sign…

Java内存模型–快速概述和注意事项

在计算中&#xff0c; 内存模型描述了线程如何通过内存进行交互&#xff0c;或更一般地&#xff0c;它指定了为分段内存或分页内存平台生成代码时允许编译器进行的假设。 在给定程序和该程序的执行跟踪的情况下&#xff0c;它实质上描述了执行跟踪是否是该程序的合法执行。 Jav…

6-7 统计某类完全平方数 (20 分)

本题要求实现一个函数&#xff0c;判断任一给定整数N是否满足条件&#xff1a;它是完全平方数&#xff0c;又至少有两位数字相同&#xff0c;如144、676等。 函数接口定义&#xff1a; int IsTheNumber ( const int N );其中N是用户传入的参数。如果N满足条件&#xff0c;则该…

C#中数组、ArrayList和List三者的区别(转) ,加修改

在C#中数组&#xff0c;ArrayList&#xff0c;List都能够存储一组对象&#xff0c;那么这三者到底有什么样的区别呢。 数组 数组在C#中最早出现的。在内存中是连续存储的&#xff0c;所以它的索引速度非常快&#xff0c;而且赋值与修改元素也很简单。 <span style"font…

phpmyadmin mysql Access denied for user 'root'@'localhost'问题解决

centos6.4 32位的vps上装了lnmp以后&#xff0c;phpmyadmin无法连接mysql服务器&#xff0c;ssh命令行里mysql -uroot -p 命令后老是出现拒绝连接的情况。php程序里也是拒绝连接。尝试过修改phpmyadmin的config.inc.php文件&#xff0c;尝试过修改my.cnf文件&#xff0c;尝试过…

带有Spring和Maven教程的JAX–WS

Spring框架通过JAX-WS提供对Web服务的远程支持&#xff0c;实际上&#xff0c;如Spring 参考文档中所述 &#xff0c;有三种将Spring POJO服务公开为JAX-WS Web服务的方式&#xff1a; 公开基于Servlet的Web服务&#xff08;适用于Java EE 5环境&#xff09; 导出独立的Web服…

7-2 然后是几点 (15 分)

7-2 然后是几点 (15 分) 有时候人们用四位数字表示一个时间&#xff0c;比如 1106 表示 11 点零 6 分。现在&#xff0c;你的程序要根据起始时间和流逝的时间计算出终止时间。 读入两个数字&#xff0c;第一个数字以这样的四位数字表示当前时间&#xff0c;第二个数字表示分钟…

CXF学习(2) helloworld

0.新建一个项目取名wsserver. pom.xml 文件如下 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd…