为了对我的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

入侵Jasper以获取JSP页面的对象模型相关推荐

  1. jasper 获取当前日期_入侵Jasper以获取JSP页面的对象模型

    jasper 获取当前日期 为了对我的JSP执行一些检查和统计分析,我需要一个包含在其中的元素的类似于DOM的层次模型. 但是,解析JSP页面并不是一件容易的事,最好留给它一个出色的工具-Tomcat ...

  2. jsp页面实现打印 .

    报表在信息系统中占据了极为重要的位置,广义上讲,报表主要以多样的格式呈现和打印动态数据,动态数据主要指保存在数据库中的数据,也可以是文本中的数据.XML数据.Hibernate数据.EJB数据.自定义 ...

  3. web页面jsp页面的打印

    报表在信息系统中占据了极为重要的位置,广义上讲,报表主要以多样的格式呈现和打印动态数据,动态数据主要指保存在数据库中的数据,也可以是文本中的数据.XML数据.Hibernate数据.EJB数据.自定义 ...

  4. jasp报错_jetty启动访问jsp页面报错

    jfinal3.5,jfinal jetty-server 2018.11,然后引入了apache-jsp和apache-jstl,访问jsp页面时还是提示org.apache.jasper.Jasp ...

  5. SpirngMVC jsp页面空指针

    SpirngMVC jsp页面空指针 ,这个错误基本上是jar包冲突,我看网上很多的方案 <dependency><groupId>javax.servlet</grou ...

  6. org.apache.jasper.JasperException: /jsp/menu.jsp (line: 8, column: 1) Page directive

    org.apache.jasper.JasperException: /jsp/menu.jsp (line: 8, column: 1) Page directive jsp页面编码设置错误,检查设 ...

  7. struts2:JSP页面及Action中获取HTTP参数(parameter)的几种方式

    本文演示了JSP中获取HTTP参数的几种方式,还有action中获取HTTP参数的几种方式. 1. 创建JSP页面(testParam.jsp) <%@ page language=" ...

  8. JSP页面是否需要重新编译--checkInterval

    Jasper用class org.apache.jasper.servlet.JspServlet实现. 初始化参数被配置好了. <servlet><servlet-name> ...

  9. 普通的Spring Web项目正常启动,在访问某些JSP页面时,页面会报错 http://java.sun.com/jsp/jstl/core

    问题描述:普通的Spring Web项目正常启动,但是在访问某些JSP页面时,页面会报错出现:org.apache.jasper.JasperException: The absolute uri: ...

最新文章

  1. LeetCode集锦(八) - 第26题 Remove Duplicates From Sorted Array
  2. ionic3 动态设置tabs页面底部导航栏隐藏,并显示输入框添加评论
  3. Java中的Error和Exceptiond的异同点
  4. python中的文件读取注意事项
  5. PHP无法执行MySQL语句,解决PHP执行批量MySQL语句的问题
  6. (转)使用Spring的注解方式实现AOP的细节
  7. ORACLE中null的排序问题
  8. 博客堂服务器转移成功!
  9. Uniapp IM即时通讯 - GoEasy集成私聊和群聊
  10. Tomcat 修改启动端口号
  11. Visual Studio x64 编译 .asm 文件方法
  12. C语言中文件指针,文件位置指针,详细解析
  13. cur前缀_常用词根前缀
  14. 错误处理(一)—— 被呼叫方拒绝接收呼叫。 (异常来自 HRESULT:0x80010001 (RPC_E_CALL_REJECTED))
  15. mysql cmd insecure_看各路神仙如何大战MySQL insecure warning报警有感
  16. 好奇,什么水平才能任教清华计算机系?
  17. fsck.ext3:unable to resolve 'LABLE=/home'
  18. android 4.4.4最新微信,微信旧版本安卓4.4.4可用
  19. 常用Java数据库连接池性能测试
  20. 《读九章算术学Python》如何用Python编程实现盈不足术?附图解分析、代码实现和习题解答

热门文章

  1. buildpack_使用Buildpack容器化Spring Boot应用程序
  2. rxjava 并行_使用RxJava和Completable并行执行阻塞任务
  3. python antlr_使用ANTLR在5分钟内用Java解析任何语言:例如Python
  4. 实践与反思_在行动中反思的实践
  5. 设计模式示例_介体设计模式示例
  6. spring总结_Spring综合课程总结
  7. rxjava 循环发送事件_使用RxJava和SseEmitter进行服务器发送的事件
  8. java面试题2014_Java生态系统– 2014年我的5大亮点
  9. 以Spring方式构建企业Java应用程序
  10. Java应用程序中的验证