Bug 35351 - error <jsp:useBean> tags
error <jsp:useBean> tags
Status: RESOLVED FIXED
Product: Tomcat 5
Classification: Unclassified
Component: Jasper
5.5.12
All All
: P2 normal (vote)
: ---
Assigned To: Tomcat Developers Mailing List
:
: 38232 (view as bug list)
Depends on:
Blocks:
  Show dependency tree
 
Reported: 2005-06-14 06:41 UTC by Theo Cleminson
Modified: 2009-05-13 03:45 UTC (History)
1 user (show)



Attachments
testcase (exploded web app) (16.50 KB, application/zip)
2005-06-14 07:11 UTC, Theo Cleminson
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Theo Cleminson 2005-06-14 06:41:23 UTC
Jasper does not support nested class references in <jsp:useBean> tags. 

For example, neither of the following nested class references are permitted:
1) <jsp:useBean id="viewBean" class="nested.TestComponent$InnerViewBean" />
2) <jsp:useBean id="viewBean" class="nested.TestComponent.InnerViewBean" />

In detail:
1) Using "nested.TestComponent$InnerViewBean" results in:
  An error occurred at line: 8 in the jsp file: /inner_viewbean1.jsp
  Generated servlet error:
  The nested type nested.TestComponent$InnerViewBean cannot be referenced using
its binary name
 
org.apache.jasper.compiler.DefaultErrorHandler.javacError(DefaultErrorHandler.java:84)
    org.apache.jasper.compiler.ErrorDispatcher.javacError(ErrorDispatcher.java:328)
    org.apache.jasper.compiler.JDTCompiler.generateClass(JDTCompiler.java:397)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:288)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:267)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:255)
    org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:556)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:293)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:291)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:241)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:802)

2) Using "nested.TestComponent.InnerViewBean" results in:
  org.apache.jasper.JasperException: /inner_viewbean2.jsp(8,0) 
  The value for the useBean class attribute nested.TestComponent.InnerViewBean
is invalid.
   
org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:39)
    org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:405)
    org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:146)
    org.apache.jasper.compiler.Generator$GenerateVisitor.visit(Generator.java:1223)
    org.apache.jasper.compiler.Node$UseBean.accept(Node.java:1116)
    org.apache.jasper.compiler.Node$Nodes.visit(Node.java:2163)
    org.apache.jasper.compiler.Node$Visitor.visitBody(Node.java:2213)
    org.apache.jasper.compiler.Node$Visitor.visit(Node.java:2219)
    org.apache.jasper.compiler.Node$Root.accept(Node.java:456)
    org.apache.jasper.compiler.Node$Nodes.visit(Node.java:2163)
    org.apache.jasper.compiler.Generator.generate(Generator.java:3270)
    org.apache.jasper.compiler.Compiler.generateJava(Compiler.java:189)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:286)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:267)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:255)
    org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:556)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:293)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:291)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:241)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
Comment 1 Theo Cleminson 2005-06-14 06:43:40 UTC
Tomcat v5.0 also produces similar errors to comment 1, eg:

1) Using "nested.TestComponent$InnerViewBean" results in:
  An error occurred at line: 8 in the jsp file: /inner_viewbean1.jsp
  Generated servlet error:
 
C:\apache\tomcat_50\work\Catalina\localhost\NestedViewBean\org\apache\jsp\inner_005fviewbean1_jsp.java:56:
cannot resolve symbol
  symbol  : class TestComponent$InnerViewBean 
  location: package nested
  perchance you meant 'TestComponent.InnerViewBean'
            viewBean = new nested.TestComponent$InnerViewBean();
                                 ^
  3 errors
                
org.apache.jasper.compiler.DefaultErrorHandler.javacError(DefaultErrorHandler.java:84)
                
org.apache.jasper.compiler.ErrorDispatcher.javacError(ErrorDispatcher.java:332)
                
org.apache.jasper.compiler.Compiler.generateClass(Compiler.java:412)
                 org.apache.jasper.compiler.Compiler.compile(Compiler.java:472)
                 org.apache.jasper.compiler.Compiler.compile(Compiler.java:451)
                 org.apache.jasper.compiler.Compiler.compile(Compiler.java:439)
                
org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:511)
                
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:295)
                
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:292)
                 org.apache.jasper.servlet.JspServlet.service(JspServlet.java:236)
                 javax.servlet.http.HttpServlet.service(HttpServlet.java:802)

2) Using "nested.TestComponent.InnerViewBean" (as for comment 1)
Comment 2 Theo Cleminson 2005-06-14 07:11:03 UTC
Created attachment 15397 [details]
testcase (exploded web app)

To use - unzip and place in WebApps/NestedViewBean/...
Invoke using: http://localhost:8080/NestedViewBean/index.jsp or similar

click the following links:
1) Inner view bean test ($)
2) Inner view bean test (.) 

reproduces errors defined above.
Comment 3 Theo Cleminson 2005-06-14 07:33:05 UTC
Request support for both reference styles described in comment 1 as there is
some ambiguity as to the correct notation in this case.
Comment 4 Remy Maucherat 2005-06-14 09:53:31 UTC
The class used must be a JavaBean. Way 2) is the proper one. Please use
tomcat-user for these kind of issues.
Comment 5 Jens Vo 2005-09-05 16:34:33 UTC
The same problem occurs when passing an instance of an inner class
to a tag file as an <%@attribute>.

E.g. you have nested tag files:

File list.tag:

<%@ taglib prefix="list" tagdir="/WEB-INF/tags/list" %>
<%@ attribute name="list" type="java.util.Map" %>
<table border="1">
  <c:forEach items="${list}" var="entry" >
    <list:row entry="${entry}"/>
  </c:forEach>
</table>

File row.tag:

<%@ attribute name="entry" type="java.util.Map$Entry"
              required="true" %>
<tr>
  <td>${entry.key}</td>
  <td>${entry.value}</td>
</tr>

You get the same error as described above; replacing the
"$" in row.tag by "." has no effect either.
Comment 6 Kin-Man Chung 2005-12-09 03:42:25 UTC
There are 3 attributes (type, class, and beanName) in useBean that can
potentially specify an inner class.  The spec isn't clear if binary names
(containing $) or canonical names (uses .) should be used with these attributes.
 My guess is the "type" attribute is intended for specifying the type of the
variable, and should use canonical names.  The attributes "class" and "beanName"
are ientended for classLoaders etc, so they should use binary names.  The only
problem is when the "type" attribute is not specified, in which case the spec
says it should use the value for the "class" attribute.  Therefore there should
be a translation from the binary name to the canonical name here.

I'll see if I can find a way to fix this, in the mean time, use the following
workaround:

To specify an inner class in <jsp:useBean>, use both the "type" and "beanName"
attributes.  Use canonical names for the "type" attribute, and binary names for
the "beanName" attribute.
Comment 7 Kin-Man Chung 2005-12-13 20:50:22 UTC
I have the fix from glassfish, so the lines can be off.

RCS file:
/cvs/glassfish/appserv-webtier/src/java/org/apache/jasper/compiler/Generator.java,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -r1.12 -r1.13
--- Generator.java      8 Dec 2005 01:28:41 -0000       1.12
+++ Generator.java      13 Dec 2005 19:35:08 -0000      1.13
@@ -1173,8 +1173,48 @@
             String type = n.getTextAttribute("type");
             Node.JspAttribute beanName = n.getBeanName();
  
-            if (type == null) // if unspecified, use class as type of bean
-                type = klass;
+            // If "class" is specified, try an instantiation at compile time
+            boolean generateNew = false;
+            String canonicalName = null;    // Canonical name for klass
+            if (klass != null) {
+                try {
+                    Class bean = ctxt.getClassLoader().loadClass(klass);
+                    if (klass.indexOf('$') >= 0)  {
+                        // Obtain the canonical type name
+                        canonicalName = JspUtil.getCanonicalName(bean);
+                    } else {
+                        canonicalName = klass;
+                    }
+                    int modifiers = bean.getModifiers();
+                    if (!Modifier.isPublic(modifiers) ||
+                        Modifier.isInterface(modifiers) ||
+                        Modifier.isAbstract(modifiers)) {
+                        throw new Exception("Invalid bean class modifier");
+                    }
+                    // Check that there is a 0 arg constructor
+                    bean.getConstructor(new Class[] {});
+                    // At compile time, we have determined that the bean class
+                    // exists, with a public zero constructor, new() can be
+                    // used for bean instantiation.
+                    generateNew = true;
+                } catch (Exception e) {
+                    // Cannot instantiate the specified class, either a
+                    // compilation error or a runtime error will be raised,
+                    // depending on a compiler flag.
+                    if
(ctxt.getOptions().getErrorOnUseBeanInvalidClassAttribute()) {
+                        err.jspError(n, "jsp.error.invalid.bean", klass);
+                    }
+                    if (canonicalName == null) {
+                        // Doing our best here to get a canonical name
+                        // from the binary name, should work 99.99% of time.
+                        canonicalName = klass.replace('$','.');
+                    }
+                }
+                if (type == null) {
+                    // if type is unspecified, use "class" as type of bean
+                    type = canonicalName;
+                }
+            }
  
             String scopename = "PageContext.PAGE_SCOPE"; // Default to page
             String lock = "_jspx_page_context";
@@ -1244,43 +1284,23 @@
                 /*
                  * Instantiate the bean if it is not in the specified scope.
                  */
-                boolean generateNew = false;
-                if (beanName == null) {
-                    try {
-                        Class bean = ctxt.getClassLoader().loadClass(klass);
-                        int modifiers = bean.getModifiers();
-                        if (!Modifier.isPublic(modifiers) ||
-                            Modifier.isInterface(modifiers) ||
-                            Modifier.isAbstract(modifiers)) {
-                            throw new Exception("Invalid bean class
modifier");-                        }
-                        // Check that there is a 0 arg constructor
-                        bean.getConstructor(new Class[] {});
-                        generateNew = true;
-                    } catch (Exception e) {
-                        // Cannot instantiate the specified class
-                        if
(ctxt.getOptions().getErrorOnUseBeanInvalidClassAttribute()) {
-                            err.jspError(n, "jsp.error.invalid.bean", klass);
-                        }
-                    }
-                }
                 if (!generateNew) {
-                    String className;
+                    String binaryName;
                     if (beanName != null) {
                         if (beanName.isNamedAttribute()) {
                             // If the value for beanName was specified via
                             // jsp:attribute, first generate code to evaluate
                             // that body.
-                            className =
+                            binaryName =
                                 generateNamedAttributeValue(
                                     beanName.getNamedAttributeNode());
                         } else {
-                            className =
+                            binaryName =
                                 attributeValue(beanName, false, String.class);
                         }
                     } else {
                         // Implies klass is not null
-                        className = quote(klass);
+                        binaryName = quote(klass);
                     }
                     out.printil("try {");
                     out.pushIndent();
@@ -1289,7 +1309,7 @@
                     out.print(type);
                     out.print(") java.beans.Beans.instantiate(");
                     out.print("this.getClass().getClassLoader(), ");
-                    out.print(className);
+                    out.print(binaryName);
                     out.println(");");
                     out.popIndent();
                     /*
@@ -1305,7 +1325,7 @@
                     out.pushIndent();
                     out.printin("throw new ServletException(");
                     out.print("\"Cannot create bean of class \" + ");
-                    out.print(className);
+                    out.print(binaryName);
                     out.println(", exc);");
                     out.popIndent();
                     out.printil("}"); // close of try
@@ -1314,7 +1334,7 @@
                     // Generate codes to instantiate the bean class
                     out.printin(name);
                     out.print(" = new ");
-                    out.print(klass);
+                    out.print(canonicalName);
                     out.println("();");
                 }
                 /*
Comment 8 william.barker 2005-12-14 08:16:10 UTC
(In reply to comment #7)
> I have the fix from glassfish, so the lines can be off.

Just to be clear, is this patch is still covered by your CCLA?  In English, is 
this a grant from glassfish to that ASF?
Comment 9 Kin-Man Chung 2005-12-15 00:57:04 UTC
(In reply to comment #8)
> (In reply to comment #7)
> > I have the fix from glassfish, so the lines can be off.
> 
> Just to be clear, is this patch is still covered by your CCLA?  In English, is 
> this a grant from glassfish to that ASF?
> 

This is just a patch, and there is no license or copyright, so YES.
Comment 10 william.barker 2005-12-15 05:17:00 UTC
Kin-Man's patch has been applied to SVN trunk, and will appear in 5.5.15.

(and, yes, the line numbers are off :).
Comment 11 william.barker 2006-01-12 05:08:00 UTC
*** Bug 38232 has been marked as a duplicate of this bug. ***
Comment 12 Mike Morris 2009-05-13 03:41:05 UTC
Regression in 6.0.18.

<jsp:useBean id="event" scope="request" type="za.co.cocosoft.etude.server.model.EventData.NullEvent">

result in:

/home/mike/src/etude/build/generated/src/org/apache/jsp/event/detail_jsp.java:2258: package za.co.cocosoft.etude.server.model.EventData does not exist
        za.co.cocosoft.etude.server.model.EventData.NullEvent event = null;
/home/mike/src/etude/build/generated/src/org/apache/jsp/event/detail_jsp.java:2260: package za.co.cocosoft.etude.server.model.EventData does not exist
          event = (za.co.cocosoft.etude.server.model.EventData.NullEvent) _jspx_page_context.getAttribute("event", PageContext.REQUEST_SCOPE);
Comment 13 Mike Morris 2009-05-13 03:45:44 UTC
Forgive me! The bug was mine.... sorry for the time-waste.