How spring rest recognizes a new session? - java

I have a problem to understand how spring rest recognizes a new session. Let's assume we have simple session bean defined like that:
#SessionScope
public class Counter {
private int counter;
public int inc() {
return counter++;
}
}
We have also a simple rest controller that provides get method that calls inc() method of the Counter and returns its current value.
I was expecting that every single request (made in postman) will result in getting value 1 since there is no way to recognize the session. For instance there is no JSESSION_ID cookie like it was in traditional servlet-based app. What I actually get is increased value of the counter and it does not matter if I make request in postman, web-browser or curl. Even using browser in private mode still refers to the same instance of the bean.
The question is how spring recognizes new sessions and how I can simulate different sessions in my "test" envinronment.

Answer found - bean was missing #Component annotation. With that JSESSION_ID cookie is created and obviously this is the way spring recognizes session.

Related

How to schedule a task to call method that will subsequently call a JSON API

I am working on an application where I need to store a bunch of products' info from a corresponding JSON API call into a cache (eh-cache 3).
The way it works is, http requests will come and trying to get the product's info. Internally a method will be called to get the info from the aforementioned JSON API web service. Since the method is marked cacheable, it will check the cache first. Will assume we know how cacheable works here.
Now the problem that I am facing is that the cache contents doesn't get refreshed until there is a request coming in that will access the products' info (therefore accessing the cache). Even though the cache expiry limit is already reached.
So, to solve this issue I have used a scheduler to call the method that will clear the cache and repopulate it by calling the same method that used earlier to get the products' info.
Like so:
#Scheduled(fixedRate = 100000)
public void refreshProdInfoCache() throws Exception {
String cacheName = "prodInfoCache";
LOGGER.info("Cache Name -> " + cacheName);
cacheManager.getCache(cacheName).clear();
cacheManager.getCache(cacheName).put(cacheName, prodService.getProdInfo());
}
However, I am getting the following exception when running the application:
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:312)
at org.springframework.web.context.support.WebApplicationContextUtils.access$400(WebApplicationContextUtils.java:65)
at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:328)
at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:323)
at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:301)
at com.sun.proxy.$Proxy146.getHeader(Unknown Source)
Have anyone any experience on this situation before? Technically I need to refresh the cache contents during non-peak hour. Thus the need to refresh the cache without waiting for users' request.
You can use cron for your purpose.
First of all, add it to the method, then enable Schedule in your Application class.
#Scheduled(cron = "0 0 0/1 * * *")
public void fetchData() {
}
#EnableScheduling
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Used a token to secure API without authentification

Objective
When a person creates a resource (no need to connect), she receives a unique token, which she must then transmit to each request she sends for information about her resource.
Question
There is a simple way to do that with Spring? Indeed, all tuto I found and read used an authentification with username and password.
Already tried
My first idea was to create a token at the end of POST methods (store it into database), put it into each GET requests and check if requestToken == databaseToken.
However, I don't think that's the best way to do it.
So, can you help me and advise me to solve the problem?
Thanks a lot!
There are multiple ways.
Using the #SessionAttributes annotation:
The first time our controller is accessed, Spring will instantiate an instance and place it in the Model. Since we also declare the bean in #SessionAttributes, Spring will store the instance.
You will get it inside controller's handler method thru #ModelAttribute.
Or, you can try this route:
#RequestMapping(value = "/test")
public String handler(HttpSession httpSession) {
httpSession.getId(); //this will give you unique identifier that you can set back to object that you send to front end and can share the same ID between requests.
}
https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSession.html#getId--

Spring session scope bean reset between requests when accessing through zuul

The spring app has a session bean, which contains certain data. That data is loaded from DB at init request to the controller, which is always a first request client calls at start up. That data is used for other requests by same user. Now, everything works fine on its own. However after trying to integrate the app into the system using zuul (which as far as I understand in this context simply redirects request from one url into another), it broke. Whenever a method is called after the init, the session bean's data is null.
Here is a snippet from service class:
#Autowired
TaskCache cache;
#Override
public void initUserSession() {
List<Task> data = loadTasks();
cache.setTasks(data);
LinearFilterStack<Task> fs = createFilterStack(data);
cache.setFilterStack(fs);
System.out.println(cache.hashCode()); //hashcode stays same
System.out.println(cache.getFilterStack() == null) //false
}
#Override
public List<Task> getTasks(Sort sort) {
System.out.println(cache.hashCode()); //hashcode stays same
System.out.println(cache.getFilterStack() == null) //true
LinearFilterStack<Task> fs = cache.getFilterStack();
List<Task> tasks = fs.filter(cache.getTasks()); //Obviously NPE
sortTasks(tasks, sort);
return tasks;
}
#Component
#Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.TARGET_CLASS)
public class TaskCache { ... }
And again, this only happens through zuul. I.e. if I use localhost:30022/rest/... it works, if I use localhost:8080/app/tasks/rest/... (which zuul redirects to localhost:30022/rest/...) I get NPE, because the cache bean loses its data after init request.
That could be caused by default behavior of Zuul that prevents passsing of cookie related headers.
The following is that default configuration of Zuul and it doesn't allow pass below headers to your downstream API servers.
zuul.sensitiveHeaders= Authorization,Cookie,Set-Cookie
So please try to define below properties. It will allow all your cookie related header to be passed to your API servers.
zuul.sensitiveHeaders= Authorization
You can find more details in section "Cookies and Sensitive Headers" of this document

Struts 2 + Spring put a managed spring bean in session

Consider an Struts 2 + Spring 4 project.
For each login the User object is put in session. As a very simple action it will look
public class LoginProcess implements ServletRequestAware {
#Inject
private AuthenticationServices authenticationServices;
public String execute() {
//The login method makes a new User and fills its setters
User newUser = authenticationServices.login(....);
getServletRequest().getSession().setAttribute("USER_SESSION", user);
}
}
As we manually make a new User object so it is not managed spring bean, and we can't use spring features in User class: #Inject , #Value ,...
I tried to change user as:
#Named
#Scope(value="session")
public class User { ...
#Inject
private AccountServices accountServices;
}
and inject the User in instead of calling new User, but I get the error:
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:91)
Well although it describes the error, but I can not find how can I fix it, and I am not sure if this is the correct way at all. It seems that I can only use spring session scope been when I am using spring mvc
Any comments ?!
Why I need this ?! (Simplified situation)
The user object has a getAccounts() methods which get all user accounts. Getting user accounts is an expensive operation, and it is possible that a user does not require its accounts during its login.
So, instead of get user accounts as soon as user logs in, we let the get method get user accounts if it does not have it:
public class User() {
private Accounts accounts;
#Inject
private AccountServices accountServices;
Accounts getAccounts() {
if (accounts == null) {
accounts = accountServices.getUserAccountsFromDB(...)
}
return accounts;
}
Don't create a new instance of User by yourself, instead get a bean from Spring context.
For example you can achieve it by implementing ApplicationContextAware interface and calling one of getBean methods.
User user = applicationContext.getBean(User.class);
// populate user and put it into session
In that way it is a Spring managed bean an all required properties should be injected.
BUT consider changing your User to a simple POJO and moving all business logic (such as fetching users accounts) to some more appropriate place, in that way your model layer will be cleaner and easily testable.

How to initialise a session in Play

Well, this is from a developer newly using Play. When it came to using session, I found its not at all like I have been doing in servlets or jsps.
I have tried reading documentation and found session in Play are stored in HTTP cookies rather. I have tried importing HTTP class of play.
My problem however is I am unable to initialise a new session to set values in it.
I have obviously tried using 'new' session as in Java and that obviosly didnt work out.
Session session = new session();
Also after looking somewhere I have used:
Session session = Http.Context.current().session();
which shows me error in identifying context and current
I have tried looking at sample codes and codes on net. each of them however is different and I don't get the basic way of using sessions in Play, so that after that I can use put and get to keep and retrieve.
I know the question seems too basic but believe me there is no exact answer available anywhere to what I need. So please help me regarding this.
Any answer, any piece of code, or any Link on this will be highly appreciated.
Forget everything about the sessions from the jsp and servlets world while working with the Play's session. Play doesn't store anything on the server side and by design it's completely stateless. The Play session is just a cookie attached to every http request and it's stored on the client side. Word 'session' may be misleading in your case.
Working with the session is pretty straight forward. All you need is inherited from play.mvc.Controller which you have to extend when creating your own controller. To put a value in it you simply call the session(String key, String value) method from within a controller. For example:
public class Application extends Controller {
public static Result login() {
session("key", "example value");
return ok("Welcome!");
}
}
If there is no session cookie stored on client side this method will create new one and attach it to the HTTP response. Otherwise it will modify the existing one.
To read stored value use:
String value = session("key");
You can also remove value from the session:
session().remove("key");
or completely destroy it:
session().clear();
These are helper methods to work with the particular cookie witch in Play's terminology is called session. Nothing stops you from creating another cookie with similar purpose. But it'll require more writing. These helper methods saves your time and in many cases are more than enough.
You can specify session cookie name in your application.conf by setting session.cookieName property.
In play 2.8 the Http.Context was deprecated. This means, among other things, that the method "session()" is no longer available in a controller.
This is the updated way of doing it:
public Result info(Http.Request request) {
//This is the equivalent to the old session()
request.session() ...
}
The Http.Request needs to be passed down through the route defined in routes. More information here.

Categories

Resources