jsp:getProperty...Name was not previously introduced as per JSP.5.3的解决方法

jsp:getProperty...Name was not previously introduced as per JSP.5.3的解决方法

Java 2010-05-21 04:32:31 阅读101 评论0   字号: 订阅

我昨天看jsp的javabean部分,当按照书中所讲在a.jsp中使用
<jsp:useBean id="test" scope="reqst" class="test.TestBean"></jsp:useBean>来设置javabean的scope为reqst,session或application,并设置了javabean test属性的值后,当forward 或incl? b.jsp的时候,在b.jsp网页中不再设置<jsp:useBean id="test" scope="reqst" class="test.TestBean"></jsp:useBean>而直接使用<jsp:getProperty property="id" name="test"/>正常情况下是应该得到javabean test的id属性的值的。但是我的tomcat服务器却给我报了如下的错误:
org.apache.jasper.JasperException: jsp:getProperty for bean with name 'test'. Name was not previously introd? as per JSP.5.3
org.apache.jasper.compiler.Generator$GenerateVisitor.visit(Generator.java:1086)
org.apache.jasper.compiler.Node$GetProperty.accept(Node.java:1124)
org.apache.jasper.compiler.Node$Nodes.visit(Node.java:2361)
org.apache.jasper.compiler.Node$Visitor.visitBody(Node.java:2411)
org.apache.jasper.compiler.Node$Visitor.visit(Node.java:2417)
org.apache.jasper.compiler.Node$Root.accept(Node.java:495)
org.apache.jasper.compiler.Node$Nodes.visit(Node.java:2361)
org.apache.jasper.compiler.Generator.generate(Generator.java:3416)
org.apache.jasper.compiler.Compiler.generateJava(Compiler.java:231)
org.apache.jasper.compiler.Compiler.compile(Compiler.java:347)
org.apache.jasper.compiler.Compiler.compile(Compiler.java:327)
org.apache.jasper.compiler.Compiler.compile(Compiler.java:314)
org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:589)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:317)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:313)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:260)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
开始我以为是我的程序的错误,但我后来拿看的书的配套光盘中带的tomcat5.016服务器来试,就没有这个问题,能够正常的显示出javabean的属性。我的tomcat的版本是官网现在最新的6.026。后来在网上查了一下,发现这个问题原来是tomcat的一个bug。具体可参见apache官网的bug记录: isss.apache.org/bugzilla/show_bug.cgi

这个虽然是tomcat自己的bug,但自己也能解决这个bug(其实这个严格来说不是bug,相反像我上面所写的那样的对javabean的用法才是不规范的,是对最新jsp规范的违背,这个我后面会说),因为tomcat应该是开源的吧(我不怎么熟对这些东西,不清楚它算不算,反正从官网上可以搞到原代码)。这个看上面的错误记录就能知道出问题的程序在
org.apache.jasper.compiler.Generator$GenerateVisitor.visit(Generator.java:1086),如果不太会看这个错误信息的话,比如我这种菜鸟,还有一种办法,就是在tomcat的源代码目录上使用windowns的搜索功能,搜哪个文件里含有“Name was not previously introd? as per JSP.5.3”这样的文字,一样可以搜到Generator.java文件。这个文件在apache-tomcat-6.0.26-src/java/org/apache/jasper/compiler目录中。
找到它的1086行,这行代码所处的方法如下:
p lic void visit(Node.GetProperty n) throws JasperException {
String name = n.getTextAttribute("name");
String property = n.getTextAttribute("property");

n.setBeginJavaLine(out.getJavaLine());

if (beanInfo.checkVariable(name)) {
// Bean is defined using useBean, introspect at compile time
Class bean = beanInfo.getBeanType(name);
String beanName = JspUtil.getCanonicalName(bean);
java.lang.reflect.Method meth = JspRuntimeLibrary
.getReadMethod(bean, property);
String methodName = meth.getName();
out
.printil("out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString("
+ "((("
+ beanName
+ ")_jspx_page_context.findAttribute("
+ "/""
+ name + "/"))." + methodName + "())));");
} else if (varInfoNames.contains(name)) {
// The object is a custom action with an associated
// VariableInfo entry for this name.
// Get the class name and then introspect at runtime.
out
.printil("out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString"
+ "(org.apache.jasper.runtime.JspRuntimeLibrary.handleGetProperty"
+ "(_jspx_page_context.findAttribute(/""
+ name
+ "/"), /""
+ property
+ "/")));");
} else {
StringBuilder msg =
new StringBuilder("jsp:getProperty for bean with name '");
msg.append(name);
msg.append(
"'. Name was not previously introd? as per JSP.5.3");

throw new JasperException(msg.toString());
}

n.setEndJavaLine(out.getJavaLine());
}
看程序就能知道,就是因为在beanInfo,varInfoNames两个集合中找不到javabean的名字test,所以才抛错。原因是这个,但怎么改呢?我对tomcat并不了解,先试着跟了跟代码,了解了一下,其实分析了一下这些代码的来龙去脉对解决这个问题并没有太多帮助,但可以多少了解点tomcat的一些执行机制。这两个集合都是出自apache-tomcat-6.0.26-src/java/org/apache/jasper/compiler/PageInfo.java文件中的PageInfo类,并且是每次在jsp转换成java源文件的时候,由apache-tomcat-6.0.26-src/java/org/apache/jasper/compiler/Compiler.java文件中的Compiler类调用的generateJava()方法来new一个PageInfo并在其构造函数参数中new一个BeanRepository(beanInfo的类),varInfoNames则是在PageInfo的构造函数中new出来的,代码如下:
protected String[] generateJava() throws Exception {

String[] smapStr = null;

long t1, t2, t3, t4;

t1 = t2 = t3 = t4 = 0;

if (log.isDebugEnabled()) {
t1 = System.currentTimeMillis();
}

// Setup page info area
pageInfo = new PageInfo(new BeanRepository(ctxt.getClassLoader(),
errDispatcher), ctxt.getJspFile());
.......

PageInfo(BeanRepository beanRepository, String jspFile) {

this.jspFile = jspFile;
this.beanRepository = beanRepository;
this.varInfoNames = new HashSet<String>();

而且我用windows搜索搜了一下给beanRepository赋值的函数addBean,并没有在源代码中找到可以添加javabean的代码,varInfoNames也是一样。所以从程序上来说当forward或incl?一个新的jsp网页的时候,这两个集合中肯定不会含有前一个网页设置的javabean的名字了,所以注定会抛错。
不过好在tomcat的archive做的好,我从上面又下到了tomcat5.016的源代码,看了下这个没问题的Generator.java(jakarta-tomcat-5.0.16-src/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler目录中),它的代码如下:
p lic void visit(Node.GetProperty n) throws JasperException {
String name = n.getTextAttribute("name");
String property = n.getTextAttribute("property");

n.setBeginJavaLine(out.getJavaLine());

if (beanInfo.checkVariable(name)) {
// Bean is defined using useBean, introspect at compile time
Class bean = beanInfo.getBeanType(name);
String beanName = JspUtil.getCanonicalName(bean);
java.lang.reflect.Method meth =
JspRuntimeLibrary.getReadMethod(bean, property);
String methodName = meth.getName();
out.printil(
"out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString("
+ "((("
+ beanName
+ ")pageContext.findAttribute("
+ "/""
+ name
+ "/"))."
+ methodName
+ "())));");
} else {
// The object could be a custom action with an associated
// VariableInfo entry for this name.
// Get the class name and then introspect at runtime.
out.printil(
"out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString"
+ "(org.apache.jasper.runtime.JspRuntimeLibrary.handleGetProperty"
+ "(pageContext.findAttribute(/""
+ name
+ "/"), /""
+ property
+ "/")));");
}

n.setEndJavaLine(out.getJavaLine());
}
一对比就发现了,这里根本没有什么varInfoNames,就是如果beanInfo中没有这个javabean,就会输出如下的java代码:
out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString(org.apache.jasper.runtime.JspRuntimeLibrary.handleGetProperty(pageContext.findAttribute("test"), "id")));
而在tomcat 6.026中,就因为这里抛错而不能生成java代码。所以我就按tomcat5.016的样子进行了修改(实际上在5.016的版本中,根本没有varInfoNames这个东西的存在),去掉了对varInfoNames的判断和最后抛错的else部分,变成跟5.016相同的样子。然后按照tomcat源代码文件夹中的BUILDING.txt文件中的指示,在源文件目录中使用ant进行编译。要编译tomcat源文件首先得下载安装ant(去apache官网下来解压就行,为了使用文件可以设置一个path)。
先执行ant download,它会下载一些编译需要的东西到C:/usr/share/java目录,然后再执行ant命令,就编译ok了,然后去C:/tomcat/output/build/lib目录中,把新编译成的jasper.jar文件替换到tomcat安装目录中的lib中,至此,再执行开头所说的那种情况的程序,就能正确显示,没有问题了。

那这样修改会不会对tomcat有什么影响,我觉得应该不会。其实这个bug在上面给的apache的bug网页上已经有人给出了修改方法,不过跟我的略有不同,可以去 svn.apache.org/viewvc这个网址来看Konstantin Kolinko是怎么改的这个文件。他是在systemprops.xml文件中添加了一个org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY布尔类型的配置,然后在Generator.java中进行了这样的修改:
添加了取得那个属性的方法:
/* System property that controls if the requirement to have the object
82         * used in jsp:getProperty action to be previously "introd?"
83         * to the JSP processor (see JSP.5.3) is enforced.
84         */
85        private static final boolean STRICT_GET_PROPERTY = Boolean.valOf(
86                System.getProperty(
87                        "org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY",
88                        "tr")).booleanVal();

然后抛错的部分的修改是:
if (beanInfo.checkVariable(name)) {
1058                    // Bean is defined using useBean, introspect at compile time
1059                    Class<?> bean = beanInfo.getBeanType(name);
1060                    String beanName = JspUtil.getCanonicalName(bean);
1061                    java.lang.reflect.Method meth = JspRuntimeLibrary
1062                            .getReadMethod(bean, property);
1063                    String methodName = meth.getName();
1064                    out.printil("out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString("
1065                            + "((("
1066                            + beanName
1067                            + ")_jspx_page_context.findAttribute("
1068                            + "/""
1069                            + name + "/"))." + methodName + "())));");
1070                } else if (!STRICT_GET_PROPERTY || varInfoNames.contains(name)) {
1071                    // The object is a custom action with an associated
1072                    // VariableInfo entry for this name.
1073                    // Get the class name and then introspect at runtime.
1074                    out.printil("out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString"
1075                            + "(org.apache.jasper.runtime.JspRuntimeLibrary.handleGetProperty"
1076                            + "(_jspx_page_context.findAttribute(/""
1077                            + name
1078                            + "/"), /""
1079                            + property
1080                            + "/")));");
1081                } else {
1082                    StringBuilder msg =
1083                        new StringBuilder("jsp:getProperty for bean with name '");
1084                    msg.append(name);
1085                    msg.append(
1086                            "'. Name was not previously introd? as per JSP.5.3");
1087                   
1088                    throw new JasperException(msg.toString());
1089                }

在那个xml文件中添加的节点为:在<section name="Jasper">节点下添加:
<property name="org.apache.jasper.compiler. Generator.STRICT_GET_PROPERTY">
95          <p>If <code>tr</code>, the requirement to have the object referenced in
96          <code>jsp:getProperty</code> action to be previously "introd?"
97          to the JSP processor, as specified in the chapter JSP.5.3 of JSP 2.0 and
98          later specifications, is enforced. If not specified, the specification
99          compliant default of <code>tr</code> will be used.</p>
100        </property>

其实看了这个人的修改所做的注释,也就应该能明白为什么tomcat在6.x(当然可能还包括之前的5.x的版本,tomcat准确从什么时候改的我不清楚)的版本中要对程序做这样的修改来抛错了。因为它是为了符合最新的jsp2.0的规范,也就是上面注释中说的chapter JSP.5.3 of JSP 2.0,为此我还专门下了一个jsp2.0 specification的pdf,果然在5.3节说的就是这个问题,原文如下:
The val of the name attribute in jsp:setProperty and jsp:getProperty will
refer to an object that is obtained from the pageContext object through its findAttribute
method.
The object named by the name must have been “introd?” to the JSP
processor using either the jsp:useBean action or a custom action with an
associated VariableInfo entry for this name. If the object was not introd? in this
manner, the container implementation is recommended (but not required) to raise
a translation error, since the page implementation is in violation of the
specification.
Note – A conseqnce of the previous paragraph is that objects that are stored
in, say, the session by a front component are not automatically visible to jsp:set-
Property and jsp:getProperty actions in that page unless a jsp:useBean action, or
some other action, makes them visible.

所以我前面说,以前那种对javabean的用法其实是对最新jsp规范的违背,所以tomcat抛错是正确的,因为违背了jsp规范嘛,严格来说这根本就不是一个bug。正确的用法其实应该是在b.jsp网页中使用比如<jsp:useBean id="test" scope="reqst" class="test.TestBean"></jsp:useBean>申明了javabean之后,再使用<jsp:getProperty property="id" name="test"/>来取值,我也做过这样的试验,这样做在tomcat 6.026上面是能正确取到test的id属性的。

只是它这个为了严格顺应最新规范的改动对过去的用法就不兼容了,比如过去的某些jsp程序就运行不了了,抛错了,所以就成了bug。

在那个bug记录里最后也说了,从6.027版本以后将包含对这个问题的patch,所以让我们期待tomcat 6.027吧。

唉,想想自己真是点背,本来只是想看看jsp,学习一下嘛,谁知道就为这么点个屁事功能,居然会碰上tomcat的这个问题,结果是费了半天的劲,跟tomcat又干了一仗,虽然也并不是没有收获,但这个本来就应该是开发tomcat的人的问题,也应该由他们来负责解决,他们当初就不该把程序做死了,应该做个配置可以让tomcat对旧程序兼容。结果我自己又较劲,在这研究琢磨啊,费了半天劲的,真他妈的倒霉!滚你妈的蛋tom病猫!

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值