2

I am reading Head First Servlets and JSP, where one servlet had 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);
}

Later on, they showed how to use <jsp:useBean> and <jsp:useProperty> to get person's name: enter image description here enter image description here

I thought I understand it - if there isn't attribute at request scope means variable person will be null, which also means new Person object using default constructor will be made. Bottom line: I won't have my attribute. Luckily, I previously stored Person object at request's scope, meaning it will find it.

So I wanted to try it out and change code a little. Instead of <jsp:useBean id="person" class="foo.Person" scope="request"/>, I typed <jsp:useBean id="person" class="foo.Person" scope="application"/>. Remember that our attribute is previously stored at request's scope and not application, meaning person will be null. To my surprise, it easily found the attribute and printed Evan. How did it happen, when book clearly showed that it will search for attribute at the same scope that I wrote in scope's attribute?

Stefan
  • 969
  • 6
  • 9

2 Answers2

2

Check the following description of <jsp:getProperty> from JavaServer Pages™Specification

The value 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.

And now, check the description of findAttribute in the same document:

Searches for the named attribute in page, request, session (if valid), and application scope(s) in order and returns the value associated or null.

Thus, if the attribute, person is not found in the request scope, the search is expanded up to application scope.

<jsp:useBean id="person" class="foo.Person" scope="application"/> will create a new Person and put it into application scope if does not exist there irrespective of whether this attribute already exists in request scope.

When you use <jsp:getProperty name="person" property="name">, the search for the attribute, person will be performed on page > request > session (if valid) > application scope(s) in sequence and in a find-first approach i.e. as soon as it is found, the value is returned (i.e. no further search happens).

Since you have two Person objects with the attribute named as person: one with the name, Evan in the request scope, and another as a new Person() in the application scope, the one in the request scope is returned as a result of the way how findAttribute works.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • Thanks! I read Bogdan's answer as well. Does that mean that in my case I would have two Person objects (one in request scope made by servlet and one in application scope made by jsp) ? So when I do ``, it would "take" object from request's scope as it is **first going to be traversed**? Because of *page>request>session>application*? – Stefan Dec 31 '20 at 15:40
  • 1
    @Stefan - I've updated my answer (added some more explanation at the end). Feel free to comment in case of any further doubt. – Arvind Kumar Avinash Dec 31 '20 at 15:59
  • Thanks! One more thing I am suspicious about and confused me as well: Picture shows how **id** from and **name** from are "tied". Then why do I have to use same value ("person") in this case for this to work? Won't call findAttribute() no matter its value in name attribute? – Stefan Dec 31 '20 at 16:07
  • `name="attribute-name"` is the syntax of how attribute is declared with ``. Based on this declaration, `findAttribute` will search for this `attribute-name`. The `` uses `id="attribute-name"`. I'm not sure why the specification architects chose different names (i.e. `name` and `id`) for the same thing. Is it what you have asked in the comment above? Feel free to comment in case of any further doubt. – Arvind Kumar Avinash Dec 31 '20 at 16:17
  • No,no. I was referring to why they both must have same id and name, which is **value** "person". Shouldn't `` be stand-alone? Because it just "tries" to find attribute using `findAttribute()`. Why must it be "tied" to ``? Because when I remove ``, program doesn't work.. – Stefan Dec 31 '20 at 16:25
2

The key takeaway is that <jsp:useBean> can also create an object if it does not find one in the scope you specified. That means that:

  • if the object is not in the scope you said it was, <jsp:useBean> will create a new object.
  • if the object is in the scope you said it is, <jsp:useBean> will use that object.

In other words, you can deal with one object or with two, depending in which context you placed the object, and in which context you say it is.

I know, it's confusing. So let's play with some examples. I'm going to butcher the Person class a bit to show where objects get created.

package test;

public class Person {
    private String name;

    public Person() {
        try {
            throw new RuntimeException("Just to see what's going on");
        } catch (RuntimeException ex) {
            System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
            for (StackTraceElement ste : ex.getStackTrace()) {
                System.out.println(ste.toString());
            }
            System.out.println("<<<<<<<<<<<<<<<<<<<<<<<");
        }
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
}

First example: scopes match

Servlet content:

public class TestController extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        
        session.setAttribute("person", new Person());
        
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/test.jsp");
        requestDispatcher.forward(request, response);
    }
}

test.jsp:

<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
    <body>
        <jsp:useBean id="person" class="test.Person" scope="session" />
    </body>
</html>

Accessing the servlet will cause this output:

>>>>>>>>>>>>>>>>>>>>>>>>>
test.Person.<init>(Person.java:8)
test.TestController.doGet(TestController.java:21)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Thread.java:744)
<<<<<<<<<<<<<<<<<<<<<<<

Look at the first two lines and you will see only one object is created, inside the servlet. The <jsp:useBean> just retrieves this one.

Second example: scopes do not match

Servlet stays the same:

public class TestController extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        
        session.setAttribute("person", new Person());
        
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/test.jsp");
        requestDispatcher.forward(request, response);
    }
}

The JSP now uses application scope, instead of session scope where the actual person is located:

<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
    <body>
        <jsp:useBean id="person" class="test.Person" scope="application" />
    </body>
</html>

The result is now this:

>>>>>>>>>>>>>>>>>>>>>>>>>
test.Person.<init>(Person.java:8)
test.TestController.doGet(TestController.java:21)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Thread.java:744)
<<<<<<<<<<<<<<<<<<<<<<<

>>>>>>>>>>>>>>>>>>>>>>>>>
test.Person.<init>(Person.java:8)
org.apache.jsp.test_jsp._jspService(test_jsp.java:71)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
test.TestController.doGet(TestController.java:24)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Thread.java:744)
<<<<<<<<<<<<<<<<<<<<<<<

You now have two objects. One created by the servlet, as in the first example, but also another one that gets created by the JSP's generated servlet, because <jsp:useBean> couldn't find an object in application scope, so it used the class attribute to create one.

The confusion is caused by <jsp:getProperty> which searches for an object in scopes page > request > session > application, until it finds one. This is separate from what <jsp:useBean> does (as described above with the two examples).

When in doubt, refer to the JSP specs.

With that being said, note that <jsp:useBean> and <jsp:getProperty> aren't really very much used in practice. People use the JSP expression language, JSTL, or other (more powerful) custom tags. For the purpose of learning from a book and avoiding to use scriptlets (that are a bad practice), these are fine to get you started, but in practice they are not really people's favorite way of accessing objects and their properties.

Bogdan
  • 23,890
  • 3
  • 69
  • 61
  • Another one of your great answers! Appreciate it a lot Bogdan. Can you tell me why do we even write `scope` that we want inside , when it is always trying to "find it" anyways? – Stefan Dec 31 '20 at 15:32
  • 2
    `` will try to get the object from that scope (it's a getAttribute, not a findAttribute), but if it then fails, it creates an object and saves it into that scope. Read very carefully the explanation from the image you posted in your question (inside the synchronized block, which BTW is another reason to specify the scope, to have something to synchronize on). `` does the findAttribute. Once again, it's two tags you are dealing with here, that work their own way, and do their own thing. Putting them together was the confusing part. – Bogdan Dec 31 '20 at 15:41
  • Thank you again, I understand it completely now. I have one core thing to ask and I am leaving you alone ;) Why is there fuss about ``, when `` doesn't care who made a Person object? **All it does is search for an object**. And why do I have to have same **value** as *name* in `` and *id* in `` for this to work? Shouldn't `` call findAttribute() regardless, as all it does is literally that? Hope you understood me :/ – Stefan Dec 31 '20 at 16:14
  • 1
    @Stefan: I saw you asked another [question](https://stackoverflow.com/questions/65524361/why-i-cant-use-jspgetproperty-without-jspusebean) about this, so I [answered there](https://stackoverflow.com/questions/65524361/why-i-cant-use-jspgetproperty-without-jspusebean/65529217#65529217) – Bogdan Jan 01 '21 at 11:19