I'm trying to figure out the best way to handle httpsession in combination with pure EJB web services. I have created a utility class and I have a controller class. Where is the best place to instantiate a utility class inside the controller class? The plan is that each user that visits/calls a webservice i.e. using the website the first time should have a httpsession object assigned to them:
public class Utility {
#Resource
private WebServiceContext wsContext;
public MessageContext mc = wsContext.getMessageContext();
public HttpSession getSession(){
return ((HttpServletRequest)mc.get(MessageContext.SERVLET_CONTEXT)).getSession(true);
}
}
#Path("controller")
#Stateless
public class ControllerEJB {
#POST
public void registerUser(
#QueryParam("fornamn") String fornamn,
#QueryParam("efternamn") String efternamn,
#QueryParam("epost") String epost,
#QueryParam("epost2") String epost2,
#QueryParam("password") String password
){
User user = new User();
user.setEmail(epost);
user.setPassword(password);
user.setFornamn(fornamn);
user.setEfternamn(efternamn);
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public String firstMethod(){
User user = new User();
user.setEmail("sam.gholizadeh");
return "unfisnihedmethod";
}
}
I've followed this tutorial http://docs.oracle.com/cd/E12839_01/web.1111/e13734/stateful.htm but as metioned earlier I'm not sure how and where to implement the logic that keeps track if a visitor has been assigned a session id or not.
Edit: Should the controller class be stateful or stateless?
Since you are using JAX-RS, it's even easier to directly get the HttpServletRequest using JAX-RS:
#Path("controller")
#Stateless
public class ControllerEJB {
#POST
public void registerUser(
#QueryParam("fornamn") String fornamn,
#QueryParam("efternamn") String efternamn,
#QueryParam("epost") String epost,
#QueryParam("epost2") String epost2,
#QueryParam("password") String password,
#Context HttpServletRequest request){
HttpSession session = request.getSession(true);
...
}
}
Related
I have two controllers. I'm trying to put the logged user into the session in one method, and then get it in a other method. But the sessions are different, how to fix it?
#RestController
public class UserController {
#Autowired
private UserService userService;
#RequestMapping(value = "/user/signIn", method = RequestMethod.POST)
public ResponseEntity<DataUser> signIn(#RequestBody #Valid SignInUser signInUser,
HttpSession session) {
User user = userService.getUser(signInUser.getEmail(), signInUser.getPassword());
session.setAttribute("user", user);
DataUser dataUser = new DataUser((User) session.getAttribute("user"));
return ResponseEntity.ok(dataUser);
}
}
#RestController
public class MessageController {
#Autowired
private MessageService messageService;
#RequestMapping(value = "/data/message", method = RequestMethod.POST)
public Message save(#RequestBody NewMessage newMessage,
HttpSession session) {
System.out.println(session.getAttribute("user"));
Message message = new Message(newMessage);
LocalDateTime dateTime = LocalDateTime.now();
message.setDateTime(dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
message.setNumberRating(0);
return messageService.save(message);
}
}
session.getAttribute("user") is null
The common behavior of sessions for WebApps is that your client is identified, commonly through a cookie called JSESSIONID, but for REST calls you do not have such possibility you probably don't even call from a browser, so you can not say that one request is coming from the same "machine/user" as this other request.
In order to do that you'll need to:
Properly configure and enable spring session
Have a way identify your requests, unique IDs of some sort.
And every new request have to inform you the same identificator, so you can ask for spring something like "give me the session for this user".
Here is a more detailed tutorial for Spring Session.
I need to validate HttpSession (for Spring MVC Application) in a better way for my current Project.
Here is the Scenario:
1) Once user is successfully validated, userObject object is added to httpSession class
HttpSession session = req.getSession(true);
session.setAttribute(AppConstants.LOGGEDIN_PARAM, userDetail);
2) Then for each request, userObject is retrieved from HttpSession Class to validate user Session
#RequestMapping(value = "/apply", method = RequestMethod.GET)
public String getTourApplyPage(HttpServletRequest req, ModelMap map) {
UserDetailTO userDetail = (UserDetailTO) req.getSession().getAttribute(AppConstants.LOGGEDIN_PARAM);
Long employeeId = userDetail.getUserType() == 1 ? userDetail.getEmployeeId():userDetail.getUserId();
if (employeeId == 0) {
req.setAttribute(AppConstants.MSG_PARAM, "Invalid employee Id.");
return userDetail.getUserType() == 1 ? AppConstants.PIS_MESSAGE : AppConstants.ADMIN_PIS_MESSAGE;
}
...
}
There can be better approaches to set userDetail object inside HttpSession but I had a restriction to not change this implementation (Point 1).
Can it possible to change getting a better implementation for getting a userDetail object from HttpSession (Point 2)?
Is it possible to write a better implementation for getting a userDetail object from httpSession?
Working at such a high level of abstraction, as controllers are at, you don't necessarily need to inject neither an HttpServletRequest nor an HttpSession.
You can make your controller session-scoped and inject a session-scoped bean there. The bean can hold a userDetails and a message for failed validations.
#RestController
#Scope("session")
public class Controller {
#Autowired
private SessionDetails details;
#PostMapping(path = "/validate")
public void validate() {
details.setUserDetails(...);
}
#GetMapping(path = "/apply")
public String apply() {
final UserDetailTO userDetails = details.getUserDetails();
...
}
}
#Component
#Scope("session")
class SessionDetails {
private String message;
private UserDetailTO userDetails;
// getters & setters
}
I read BalusC's excellent tutorial on JSF communication and it helped me establish the basics of my app. I would like to share the currently logged in User object that is set in the SessionScoped BaseBean class with all of its subclasses. Can this be done without injecting BaseBean as a #ManagedProperty for every single backing bean that needs to reference the logged in User?
My classes are listed below. Please let me know if more info is needed and I will be happy to update my question.
BaseBean Class
All other beans are subclasses of this bean. I am doing this to allow code reuse between beans.
#ManagedBean
#SessionScoped
public class BaseBean {
#EJB
protected UserDao userDao;
// Other DAOs along with methods (like isLoggedIn()) shared between beans
private User loggedInUser;
public User getLoggedInUser() {
return loggedInUser;
}
public void setLoggedInUser(User user) {
loggedInUser = user;
}
public boolean isLoggedIn() {
return loggedInUser != null;
}
}
LoginBean Class
Backing bean for the login page. To reduce number of DB calls I used the #ManagedProperty approach from the above tutorial to set the User object in the #SessionScoped BaseBean. Right now logging in and setting loggedInUser works as expected.
#ManagedBean
#RequestScoped
public class LoginBean extends BaseBean {
#ManagedProperty(value = "#{baseBean}")
protected BaseBean baseBean;
private String username;
private String password;
public String login() {
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.login(username, password);
} catch (Exception e) {
e.printStackTrace();
} finally {
baseBean.setLoggedInUser(userDao.getUser(username));
}
return "index";
}
public String getUserFirstName() {
return baseBean.getLoggedInUser().getFirstName();
}
// Getters and Setters, including for the #ManagedProperty baseBean.
}
CreateReport Class
This is an example of one backing bean from many. I want to reference the currently logged in User in order to create a report, however if the below code runs the User will be null! The only way I can get it to work is by adding a #ManagedProperty entry with getters and setters for BaseBean just like in LoginBean class. I would really like to avoid this as I will be copy-pasting this code to almost every single backing bean that I have!
#ManagedBean
#RequestScoped
public class CreateReport extends BaseBean {
private Report report = new Report();
public String createReport() {
report.setOwner(getLoggedInUser()); // Use inherited method
// instead of DI-ing BaseBean
reportDao.create(report);
return "index";
}
}
Used Software
Glassfish 4
Mojarra 2.2
Edit
One solution that I found is getting the instance of BaseBean from the FacesContext directly (I guess somehow the other beans are not in the same context or "don't see it?"). The following code (from BaseBean) would do what I want, however any bean subclass would have to invoke base() and that seems awkward and wrong.
protected FacesContext context = FacesContext.getCurrentInstance();
public BaseBean base() {
return (BaseBean) context.getApplication().evaluateExpressionGet(context, "#{baseBean}", BaseBean.class);
}
I can see you want to implement authentication and authentication controls, then you can use JSF Filters. You can keep your BaseBean class with session scope, create a new class which implements javax.servlet.Filter and in this class to get your BaseBean class through of session, for example:
public class LoginFilter implements javax.servlet.Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
BaseBean base = (BaseBean) req.getSession().getAttribute("baseBean");
if (base != null && base.isLoggedIn()) {
// to do something
chain.doFilter(request, response);
} else {
// to do something
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect(req.getContextPath() + "/index.xhtml");
}
}
public void init(FilterConfig config) throws ServletException {
// to do something
}
public void destroy() {
// to do something
}
}
Now, if you want to create a report with your BaseBean class, you can get your BaseBean class of session:
BaseBean base = (BaseBean) ( FacesContext.getCurrentInstance()
.getExternalContext().getRequest()).getSession().getAttribute("baseBean") );
Then my recommendation in your case is to avoid inheritance and use the advantages of JSF.
I hope this information helps you.
Good Luck.
making BaseBean a managed bean itself, and using it as a superclass for all other managed beans are two things that should not go along.
instead, you can:
remove #ManagedBean annotation from BaseBean.
save loggedInUser to session.
keep isLoggedIn as a protected method in BaseBean. you will be able to reach session via FacesContext there and get loggedInUser from session.
((HttpServletRequest)FacesContext.getCurrentInstance()
.getExternalContext().getRequest()).getSession()
ps: i don't know what the hell i was thinking when offering a static variable.
Can I use WebServiceContext and the method getMessageContext() in a web service to get a HttpSession object for save and get a session value? I was trying use the HttpSession in a web service like this:
#WebService
#SOAPBinding(style = SOAPBinding.Style.RPC, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
public class DummyWs {
#Resource
private WebServiceContext wsContext;
#WebMethod(operationName = "sayHello")
public String sayHello(#WebParam(name = "name") String name) {
return "hello " + name;
}
#WebMethod(operationName="setValue")
public void setValue(#WebParam(name = "value") String newValue) {
MessageContext mc = wsContext.getMessageContext();
HttpSession session = ((javax.servlet.http.HttpServletRequest)mc.get(MessageContext.SERVLET_REQUEST)).getSession();
session.setAttribute("value", newValue);
}
#WebMethod(operationName="getValue")
public String getValue() {
MessageContext mc = wsContext.getMessageContext();
HttpSession session = ((javax.servlet.http.HttpServletRequest)mc.get(MessageContext.SERVLET_REQUEST)).getSession();
return (String)session.getValue("value");
}
}
I saw other examples that uses the #Stateful annotation, but I don't use this. Is necessary use the #Stateful annotation? What happened if I don't use this annotation?
Did you see the sample stateful included in the Reference Implementation zip distribution?
The sample use the annotation com.sun.xml.ws.developer.Stateful in the web service implementation and the class com.sun.xml.ws.developer.StatefulWebServiceManager for store the objects. As Java Web services are required to be stateless, we need to
save the contents of the objects in a persistent storage across client calls.
In another way, you should prefer a staless service. The failure-handling, the interaction with transactional semantics is simple. And the reusability is enhanced.
I want to read a domain object (UserVO) from session scope.
I am setting the UserVO in a controller called WelcomeController
#Controller
#RequestMapping("/welcome.htm")
public class WelcomeController {
#RequestMapping(method = RequestMethod.POST)
public String processSubmit(BindingResult result, SessionStatus status,HttpSession session){
User user = loginService.loginUser(loginCredentials);
session.setAttribute("user", user);
return "loginSuccess";
}
}
I am able to use the object in jsp pages <h1>${user.userDetails.firstName}</h1>
But I am not able to read the value from another Controller,
I am trying to read the session attribute as follows:
#Controller
public class InspectionTypeController {
#RequestMapping(value="/addInspectionType.htm", method = RequestMethod.POST )
public String addInspectionType(InspectionType inspectionType, HttpSession session)
{
User user = (User) session.getAttribute("user");
System.out.println("User: "+ user.getUserDetails().getFirstName);
}
}
The code you've shown should work - the HttpSession is shared between the controllers, and you're using the same attribute name. Thus something else is going wrong that you're not showing us.
However, regardless of whether or not it works, Spring provides a more elegant approach to keeping your model objects in the session, using the #SessionAttribute annotation (see docs).
For example (I haven't tested this, but it gives you the idea):
#Controller
#RequestMapping("/welcome.htm")
#SessionAttributes({"user"})
public class WelcomeController {
#RequestMapping(method = RequestMethod.POST)
public String processSubmit(ModelMap modelMap){
User user = loginService.loginUser(loginCredentials);
modelMap.addtAttribute(user);
return "loginSuccess";
}
}
and then
#Controller
#SessionAttributes({"user"})
public class InspectionTypeController {
#RequestMapping(value="/addInspectionType.htm", method = RequestMethod.POST )
public void addInspectionType(InspectionType inspectionType, #ModelAttribute User user) {
System.out.println("User: "+ user.getUserDetails().getFirstName);
}
}
However, if your original code isn't working, then this won't work either, since something else is wrong with your session.
#SessionAttributes works only in context of particular handler, so attribute set in WelcomeController will be visible only in this controller.
Use a parent class to inherit all the controllers and use SessionAttributes over there. Just that this class should be in the package scan of mvc.
May be you have not set your UserVO as Serializable.