I'm using the Wicket Auth/Roles and I ran into the same problem as the OP of this thread.
I need to access the DB service layer in the AuthenticatedWebSession (for user authentication). I followed Steve Flasby's suggestion and did the following:
#Override
public Session newSession(Request request, Response response) {
Session s = new MySession(request);
mInjector.inject(s);
return s;
}
Unfortunately this results in
java.lang.IllegalStateException: EntityManager is closed
(presumably due to the fact that (a) I'm using open session in view, and (b) the session spans over several requests).
I solved this by moving the injection into the AuthenticatedWebSession.authenticate method.
#Override
public boolean authenticate(String username, String pass) {
Injector.get().inject(this);
...
}
I suspect that this is not best practice, because now I need to access to the service layer in other methods too, and it doesn't seem like a good idea to add Injector.get().inject(this) in each such method.
My question:
How do I perform injection into the session object upon each request? (Or, if this is a bad approach all together, how do I access the service layer in the AuthenticatedWebSession?)
You can implement IRequestCycleListener (extend AbstractRequestCycleListener) and implement:
#Override
public void onBeginRequest(RequestCycle cycle)
{
if (Session.exists()) {
Injector.get().inject(Session.get());
}
}
Register your IRequestCycleListener in Application#init() with getRequestCycleListeners().add(new YourRequestCycleListener()).
Related
What's the best way to access Session Data in Spring Boot?
I'm developing a new microservice that included login/logout/2fa operation.
Login will consist 3 or 4 steps, like -> /validateUser (1.Step), /validateOneTimePassword (2.Step) ... and more some paths/steps.
We want to manage the session in Redis(distributed) and I added the needed conf/depend. When I make a request I can see creating a new session in Redis.
When I searched for the best way, I found some approaches.
Such as:
1:
#PostMapping("/myrequest")
public void handleMyRequest(HttpSession session, #RequestBody RequestObject requestObject) {
session.getAttribute("myKey");
...
}
2:
#PostMapping("/myrequest")
public void handleMyRequest(#RequestBody RequestObject requestObject) {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session = attr.getRequest().getSession();
session.getAttribute("myKey");
...
}
3:
#Autowired
HttpSession httpSession;
#PostMapping("/myrequest")
public void handleMyRequest(#RequestBody RequestObject requestObject) {
session.getAttribute("myKey");
...
}
Which one of these? Let's say we decided on the right thing so now I have a new question.
Using session should we pass the session parameter to the session layer? Or where do need we to make set/get session operation? Controller layer, service layer which one?
Please explain that.
Any of the 3 ways are fine but 1 and 3 will give you clean code. If multiple methods are accessing the session then number 3 would be the best.
As far as the other question is concerned, it all depends on use case. If you just need to read the values, you can extract the values in controller layer and pass it to the service layer, increasing chances of reusing the service layer code. If you need to set something in session and need to be done at the middle of business logic, you may have to send it to service layer.
I've written a custom Validation Annotation and a ConstraintValidator implementation, which uses a Spring Service (and executes a Database Query):
public class MyValidator implements ConstraintValidator<MyValidationAnnotation, String> {
private final MyService service;
public MyValidator(MyService service) {
this.service = service;
}
#Override
public void initialize(MyValidationAnnotation constraintAnnotation) {}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return service.exists(value);
}
}
It's used like this:
public class MyEntity {
#Valid
List<Foo> list;
}
public class Foo {
#MyValidationAnnotation
String id;
}
This works quite nice, but service.exists(value) is getting called for every item within the list, which is correct, but could/should be optimized.
Question:
When validating an instance of MyEntity, I'd like to cache the results of the service.exists(value) calls.
I don't want to use a static HashMap<String, Boolean>, because this would cache the results for the entire application lifetime.
Is it possible to access some kind of Constraint Validation Context, which only exists while this particular validation is running, so I can put there the cached results?
Or do you have some other solution?
Thanks in advance!
You can use Spring's cache support. There might be other parts in the application which needs caching and this can be reused. And the setup is very simple too. And it will keep your code neat and readable.
You can cache your service calls. You need to put annotation on your service methods and a little bit of configuration.
And for cache provider you can use Ehcache. You have many options like setting ttl and max number of elements that can be cached and eviction policy etc etc if needed.
Or you can implement your own cache provider if your needs are simple. And if it is web request, In this cache you may find ThreadLocal to be useful. you can do all caching for this running thread using threadlocal. When the request is processed you can clear the threadlocal cache.
I have a scenario where I need to run a few db checks at the start of every web request, and in the case of success I need to store objects for use later in the request by the controller, or in the case of failure I need to render an error page.
A very similar real world example would be a SaaS app checking and loading the account based on a vanity url, then storing the account for use by controllers to avoid multiple db requests.
What are the best ways to achieve this in a Spring boot app? I have experimented with Filters but I think an Interceptor might be better at the task, that covers running the check but what about storing the objects for later use? Is there a request lifecycle context of some kind that I can store against?
Spring supports request scope for beans. You can use them for storing data used during request execution.
In my experience, best way I've done similar stuff is through HandlerMethodArgumentResolver.
Basically imagine you have a custom type, let's call it UserContext where you store the information that's needed for the request. And you have a UserContextService let's say that has a method getUserContext(HttServletRequest), that is used to retrieve the context based on the request, from which you can call your database based on whatever request parameter/header/path-variable, etc. You can refine that as you need. But based on this simple assumptions, you can have a controller that looks like this:
#RequestMapping("/some/url")
public SomeResponse someMethod(UserContext userContext, ...) {
//do something here with UserContext
}
The way that Spring will inject this UserContext into your controller would be with a custom HandlerMethodArgumentResolver like this:
#Component
public class UserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
#Autowired
UserContextService
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(UserContext.class);
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest req = (HttpServletRequest)webRequest.getNativeRequest();
UserContext userContext = userContextService.getUserContext(req);
if (userContext != null) {
return userContext;
} else {
return WebArgumentResolver.UNRESOLVED;
//Or throw exception
}
}
}
That you'll register by overriding the WebMvcConfigurer.addArgumentResolvers method in your WebMvcConfigurer bean/config-class.
This mechanism is the same used by #PathVariable, #RequestParam, etc...
I want to use Spring + Hibernate in my web application.
My application is written without Spring.
When "open page" action is called I open Hibernate Session, store it in Http Session and share it between my DAOs. When save action is called I start transaction using my old session.
But now I want to migrate my old DAOs to HibernateDaoSupport based DAOs.
How can I share session in this case? If my DAOs reference to the one SessionFactory in beans.xml will they share the same session?
How can I manage session in this case(open new or use old)?
I have write the following code but I get
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
on page System.out.println(obj.getCategory().getName());
public class CategoryObjectDAOSpringImpl extends HibernateDaoSupport implements CategoryObjectDAO {
#Override
public CategoryObject get(int id) throws Exception {
CategoryObject obj = getHibernateTemplate().get(CategoryObject.class, id);
System.out.println(obj.getId());
System.out.println(obj.getCategory().getName());
for (ObjAttrCommon objAttr : obj.getAttributes()) {
//objAttr.setSession(getSession());
System.out.println(objAttr.getId());
}
return obj;
}
It is strange that if I add
getSessionFactory().openSession();
call at the top I have the same exeption.
I think the same awswer is valid for this post:
If your "unit of work" cannot be automatically per request, I think you can create it manually in your service layer using a transaction. Something like this:
public Object serviceMethod(params) {
TransactionTemplate transactionTemplate = getHibernateTemplate().get(CategoryObject.class, id);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
try {
// call your DAO's to update/delete/... and set values to service
} catch (DAOException e) {
LOGGER.error(e);
throw new ServiceException(e);
}
}
});
}
EDITED: So extrange that the exception appears within a transaction. Make sure that the problem is not in your view. If after you retrieve your entites using HibernateTemplate you access it from the view it will explain the LazyException, because when you access your objects from the view, your Hibernate session will be already closed.
In you're using your models in your JSP/JSF your need to extend your unit of work to cover all request context. You have to include a filter that manages your Hibernate session opening, what is called Open Session In view Pattern, take a look at this.
Copy the filter implementation that appears in this post and include it in your application, it should solve the problem.
I make (thanks with some users on this portal) my application that implements SessionAware.
This is my actual code :
public class UserManager extends ActionSupport implements SessionAware {
private Map<String, Object> session;
#Override
public String execute() throws Exception {
return SUCCESS;
}
public void setSession(Map<String, Object> map) {
this.session=map;
}
public String checkLogin() {
session.put("loggedOn", true);
return SUCCESS;
}
public String checkLogout() {
session.clear();
return SUCCESS;
}
}
And i check these variables on my .jsp :
<s:if test="#session['loggedOn']!=true">
DIV LOGIN
</s:if>
<s:else>
DIV LOGOUT
</s:else>
An easy piece of code.
What i'd like to know is this :
1 - the bean is (as default) request scoped. So when the request is finished it will be destroyed. But i see that, when i put a variable in the Map, it still alive on the server. How is possible? Is a variable of my Bean.
2 - Who call the setSession method? I think the servlet, due to the fact I implements that interface?
3 - I would like to detach about saving object/bean on a Session Object. I'd like to use the Bean session scoped (as for any kind of MVC framework). How can I do it on struts2?
Hope you can make clear these questions :) Cheers
1) Your bean is a struts2 action as such it is action scoped (which is a more restrictive scope than request). I say that action is a lesser scope because you can forward an action to another action in which case the previous action goes out of scope, request scoped objects however will stay in scope until the request is served. When you implement SessionAware you are provided with a reference to the SessionObject you are then putting your object into the Session object who's life span is much longer than your action. Clear your browser cache will remove the session value... you can also remove them by code be implementing SessionAware and removing the value from the map.
2 - The session already exists. You can get the session and add keys but even if you don't put anything in it, it will be there for use.
3 - You have a later question already for this topic see my answer there.