2

Say there is servlet that has code:

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    foo.Person p = new foo.Person("Evan");
    req.setAttribute("person", p);

    RequestDispatcher view = req.getRequestDispatcher("/result.jsp");
    view.forward(req, resp);
}

, that goes to result.jsp to print given name (Evan). Here is a picture of how it would look (source Head First Servlets and JSP):

enter image description here

I know that <jsp:useBean> returns same Person object by calling getAttribute()- since they are in same request scope. While on the other side, <jsp:getProperty> will call findAttribute() to literally try to find attribute of value "person".. and eventually print Evan.

But what if I didn't use <jsp:useBean>? Does that mean I couldn't access "person" attribute at scope request? I mean it would still be there, even if I didn't use <jsp:useBean>. So why I must have same value("person") inside both id in <jsp:useBean> and name inside <jsp:getProperty>? Simple removing <jsp:useBean> breaks my program.

Knowing that <jsp:getProperty> calls findAttribute(), wouldn't it be more logical if there was a single attribute (like attribute-name), that will be used as an argument to find attributes in scopes page>request>session>application? Why I must "tie" those two tags: <jsp:useBean> and <jsp:getProperty>?

Stefan
  • 969
  • 6
  • 9
  • The idea of the is declaring the variable that you will use and in which scope exists, if not exists Java will create. You need to define which type of class in order to know how Java need to convert the object and which attributes and methods have the class. Imagine that if Java doesn't know the type of the object can produce an exception. For more information look this [question](https://stackoverflow.com/questions/41679798/how-does-jspusebean-scope-attribute-work) – Andres Sacco Dec 31 '20 at 18:40

2 Answers2

2

What do you think of the following code?

public class Main {
    public static void main(String[] args) {
        System.out.println(person);
    }
}

You must have already correctly guessed that it won't be compiled successfully.

Now, what about the following code?

public class Main {
    public static void main(String[] args) {
        Person person = new Person();// Declaring person
        System.out.println(person);
    }
}

Of course, it will be compiled successfully1 because now the compiler understands what person is.

Similarly, using

<jsp:getProperty name="person" property="name">

without declaring person as

<!-- Declaring person -->
<jsp:useBean id="person" class="foo.Person" scope="request" />

won't be compiled successfully.


1 Assuming Person.class is there.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • Makes sense. But why not simply have class attribute inside `` (if it even existed)? It would have all details needed for *finding* + *printing* object. As it would always call `findAttribute()` starting from page>request>session>application. So honestly, I don't need ``. There must be something else I am missing, as well? – Stefan Dec 31 '20 at 20:27
  • 1
    @Stefan - Again, if I ask you why `Person person = new Person();` is required before `System.out.println(person);` and why not Java specification architects made it possible to declare as well as print/use the variable, `person` in a single statement? You may say: *we do not know and it will be waste of time trying to find the answer to this question. We can only hope that it may be possible in future versions but as of now, we have to live with it.* And, I (or anyone else) will be happy with your answer. – Arvind Kumar Avinash Dec 31 '20 at 20:34
  • Hahaha true.. So in other words `` is used for finding and printing objects, while `` is used to create/locate object at given scope and do casting. At the end, I "tie" those two statements with id and name. That is long story short. Can I just remember it in that manner? – Stefan Dec 31 '20 at 20:42
  • Yes, you got that right! You declare a variable using `id` and `class` in ``, you use it with `name` in ``. – Arvind Kumar Avinash Dec 31 '20 at 20:52
1

TL;DR: You should just remember that you need to use <jsp:getProperty> with <jsp:useBean> because the specification says so. <jsp:useBean> needs to introduce the bean to the JSP processor, before <jsp:getProperty> can use it.

The longer explanation:

Why I can't use <jsp:getProperty> without <jsp:useBean>?

Because they were "somewhat" designed to work together. I don't know why it was decided like that (only the designers of the JSP specification can answer that), but the spec itself has this to say about <jsp:getProperty>:

The object named by the name must have been “introduced” 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 introduced 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.

I say "somewhat" designed to work together, because in some cases you can use <jsp:getProperty> without <jsp:useBean>, but you have to configure the JSP processor to ignore the JSP.5.3 specification rule (for servers that allow that).

This is not very clear, so let's see what happens with some code.

I have the following JSP:

-------------------------------------------------------------------
<jsp:useBean id="person" class="test.Person" scope="application" />
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<jsp:getProperty name="person" property="name" />
-------------------------------------------------------------------

I used those delimiters so that I can later find them in the JSP generated servlet, where they result in this code:

  out.write("\t\t-------------------------------------------------------------------\r\n");
  out.write("\t\t");
  test.Person person = null;
  synchronized (application) {
    person = (test.Person) _jspx_page_context.getAttribute("person", javax.servlet.jsp.PageContext.APPLICATION_SCOPE);
    if (person == null){
      person = new test.Person();
      _jspx_page_context.setAttribute("person", person, javax.servlet.jsp.PageContext.APPLICATION_SCOPE);
    }
  }
  out.write("\r\n");
  out.write("\t\t+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r\n");
  out.write("\t\t");
  out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString((((test.Person)_jspx_page_context.findAttribute("person")).getName())));
  out.write("\r\n");
  out.write("\t\t-------------------------------------------------------------------\r\n");

If you look at the <jsp:getProperty> you can see that it does a cast to test.Person:

org.apache.jasper.runtime.JspRuntimeLibrary.toString((((test.Person)_jspx_page_context.findAttribute("person")).getName()))

But where did that came from? In your <jsp:getProperty> you specify a bean name (person) and a property name (name), but no class. So those attributes only result in findAttribute("person") and then in getName(). Where was the class taken from? And the answer is, the previous call to <jsp:useBean> introduced this in the JSP processor.

So you have to call <jsp:useBean> to introduce the bean in the JSP processor so that when the processor sees the <jsp:getProperty> it knows what it's dealing with. So basically, <jsp:useBean> defines it, then <jsp:getProperty> uses it. If you don't call <jsp:useBean>, the <jsp:getProperty> will try to use something undefined, the JSP processor will complain, and you get back an exception of:

jsp:getProperty for bean with name 'person'. Name was not previously introduced as per JSP.5.3

But if you read the specs, it says:

[...] the container implementation is recommended (but not required) to raise a translation error [...]

If you use Tomcat, for example, there is a org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY system property that controls the requirement to have the object used in <jsp:getProperty> to be previously "introduced" to the JSP processor (basically, enforcing or not the JSP.5.3 rule). See for example this Tomcat documentation page.

So, if I start my Tomcat server with a system variable of:

-Dorg.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY=false

Then I can use <jsp:getProperty> without <jsp:useBean>, PROVIDED THAT I INTRODUCE THE person BEAN IN SCOPE SOME OTHER WAY (like from a servlet with request.setAttribute(), session.setAttribute() or application.setAttribute() so that <jsp:getProperty> can do a pageContext.findAttribute() and look for a bean named person and find it.

If you use that system property, then the output generated by the <jsp:getProperty> tag changes. It no longer depends on <jsp:useBean> and the cast is removed:

out.write("\t\t+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r\n");
      out.write("\t\t");
      out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString(org.apache.jasper.runtime.JspRuntimeLibrary.handleGetProperty(_jspx_page_context.findAttribute("person"), "name")));
      out.write("\r\n");
      out.write("\t\t-------------------------------------------------------------------\r\n");

If someone is interested in how all of this mess unfolds, the classes to look at (for a Tomcat server) are: org.apache.jasper.compiler.Validator and org.apache.jasper.compiler.Generator.

Bogdan
  • 23,890
  • 3
  • 69
  • 61
  • Thanks Bogdan again! I was re-reading this answer which motivated me to go even deeper. Would you mind checking this question as a reference to this (https://stackoverflow.com/questions/65621836/why-does-jspgetproperty-need-jspusebean-but-el-doesnt)? I know you are genius in this field, so I had to poke you :) – Stefan Jan 08 '21 at 01:02
  • 1
    @Stefan: I'm not a genius in this field, I just have some experience :). I've added and answer on your new question. – Bogdan Jan 08 '21 at 09:20
  • I know I should avoid these comments on stackoverflow, but thanks! Your help really helped me move on from where I was stuck. – Stefan Jan 08 '21 at 23:54