views:

399

answers:

2

Using Spring 3 MVC and JSP, I simply want to test if a user is logged in, I'm not interested in using Spring Security currently

<jsp:useBean id="myAppUser" type="com.xxx.MyUser" beanName="myUser" scope="session" />
<c:choose>
    <c:when test="myUser.loggedIn">
        //dostuff
    </c:when>
    <c:otherwise>
        //dootherstuff
    </c:otherwise>
</c:choose>

But the problem is that when there isn't yet a myAppUser in the session, jsp:useBean throws an exception. Now I realize that I can have the JSP:useBean actually instantiate the object by giving it a class, but I don't like knowing that somewhere in some JSP fragment I have objects being instantiated and added to my session, so I either want to always set an initial value for that user, and have control over it programatically, or I'd like a way to get that bean that allows it to be null or not exist, if it doesn't exist just return null

either way would be fine

if my question points to a fundamental misunderstanding in what I should be doing please provide a link to documentation that will thoroughly explain this use case

+1  A: 

Testing for something like "is this person logged in?" by examining the Session object in the View kind of blurs the lines between the roles of the Model, the View, and the Controller.

Regardless of figuring out the correct JSTL syntax to use to not get jsp:useBean to throw an exception, I suggest moving the check for logged-in to the controller side and have the view test if a "isLoggedIn" property exists in the model.

  1. Controller checks if the session contains a user object, adds a property isLoggedIn to the model with a value of true or false.
  2. The logic in the view branches on the value of ${isLoggedIn}.

In other words, the view should be checking only the model for properties/data, and not accessing the session or other state directly.

matt b
the problem with your solution means that every controller that is associated with a page that has logic that branches on that logic (literally every single page has at least one component that needs things like the users name and so on) needs to include that in the model, so basically the controller has to include that every single time, which means it would probably be easier to just allow it to be directly accessed from the view, it's really the same thing and this would be an argument of preference. I'd agree if this isn't going to be on every page
walnutmon
also, if someone else were to write a controller method and they forget to include the users logged in status, their name, their nickname etc then exceptions will be thrown, I'd like a way to make it the default behavior if I were to go this route
walnutmon
You don't necessarily have to do this in a controller. You can also have a servlet filter do this.
palto
You can use an interceptor (http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/servlet/HandlerInterceptor.html) for this. Interceptors are useful for applying common logic to ModelAndViews from a number of controllers. Or you could have a base controller class. You have this problem either way, don't you? In the original method, every view needs the same boilerplate code to check the session. At the very least you should look into putting this code in a single place, so that if you ever change where the user is stored you don't have to change every view.
matt b
suppose I change the name of myAppUser to myAppUserManager, does that make it easier for me to get a solution? I'm sorry if this is coming off as frustration, it isn't, just trying to get an answer, by putting "isLoggedIn" on my user object I can change that method later and that will be the one place it needs to change
walnutmon
@jboyd, having a single piece of code that does this sort of thing - checking if a user is logged in - is preferrable to repeating the logic in dozens of different files. It means that making the type of change you mention, such as the name of a class, only has to be done in one place rather than every file.
matt b
The Intro to Spring MVC section in the Spring manual does a good job of explaining what the various roles of the model, view and controller should be in a well designed application. Following these outlines can save a lot of grief later. http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-introduction
matt b
+2  A: 

This is not the right approach, but in fact you could solve the particular issue with the JSTL <c:catch>.

<c:catch var="e">
    <jsp:useBean id="myAppUser" type="com.xxx.MyUser" beanName="myUser" scope="session" />
</c:catch>
<c:choose>
    <c:when test="${empty e && myUser.loggedIn}">Logged in</c:when>
    <c:otherwise>Bean doesn't exist or user is not logged in</c:otherwise>
</c:choose>

The right approach is described in answer of matt b. You really need to solve it at a higher level. Have a bean something like UserManager which has the User as child property along several methods like login(User), logout() and isLoggedIn(). If the user is logged in, then the User ought to be non-null. When the user logs out, then the User ought to be set to null.


Update as per comments: As an alternative, you can also just get rid of the whole jsp:useBean declaration and intercept on the presence of the MVC-framework injected ${myUser} in the session.

<c:choose>
    <c:when test="${myUser.loggedIn}">Logged in</c:when>
    <c:otherwise>Bean doesn't exist or user is not logged in</c:otherwise>
</c:choose>

EL will namely transparently "suppress" potential nullpointerexceptions and just return false.

BalusC
this is what I want, but I don't know how to do it, what you're describing is actually exactly what I'm doing.. the myAppUser object is a composite object of my business user code, and I'd like to have control over when it is instantiated and how, if you re-read the question you will see that what I am asking for is what you described, but the question is that I'm not sure how to implement it. You'll also notice that you psuedo solution has the same problem, if I need to say logout(user), where does the user come from?
walnutmon
There is no `logout(User)`, it's just `logout()`. The `UserManager` itself already has the logged-in `User` referenced as property, passed-in by `login(User)`.
BalusC
You're saying UserManager, I just called it User, but they are the same thing, my controller makes a service call that returns the user object which has business code in it that abstracts what being "logged in" actually means and many other things, I could call it manager I suppose, but my problem would still be the exact same. The manager may not be defined yet, because if they haven't logged in then the code that creates the manager and places it on the session hasn't been run yet. It sounds like what you're saying is to create a bean of session scope in the spring config
walnutmon
In my example, the `UserManager` is always created and present, regardless of whether the user is logged in or not. Else the creation of `jsp:useBean` is completely unnecessary. I'll update my answer to include an alternative example.
BalusC
"he UserManager is always created ans present" this is exactly what I'd like to see, thanks!
walnutmon