Spring Inject Autowired SessionAttribute into Service Layer - java

Is there a way to #Inject/#Autowired a SessionAttribute into the #Service layer directly without passing it through a #Controller?
I'm looking for something like this:
#Autowired
#SessionAttribute("userprincipal")
UserPrincipal principal;
Possible Solution:
#Configuration
public class ApplicationConfig {
#Bean
#Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserPrincipal sessionUserPrincipal() {
// not sure here the user does not exist at creation of bean
}
}

My solution, hopefully this will save someone else some time.
Caution: The injected dependency is hidden, this will cause problems if used outside a session. Use Optional<T> if this is the case and handle internally. If you share your code your team will not be aware of the required dependency.
Testing: When testing you will be required to provide the session bean for #Autowired functionality.
Session Bean Class:
public class SessionUserPrincipal implements Serializable {
private static final long serialVersionUID = 1L;
private UserPrincipal principal;
public SessionUserPrincipal() {}
// mutator methods omitted
}
return Optional<T> if session attribute is not guarantied to be available
Add Bean to Context:
#Configuration
public class WebServletContextConfiguration implements WebMvcConfigurer {
#Bean
#Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public SessionUserPrincipal sessionUserPrincipal() {
return new SessionUserPrincipal();
}
}
Add RequestContextListener to web.xml
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
this is a requirement for the code below to work. It exposes state necessary to implement session scope. By default that state is exposed by DispatcherServlet, so it's not available before request enters DispatcherServlet (Spring Security filters). You will get an exception if you try to #Autowire a session bean before its available.
Add session attribute to session #Bean on successful authentication.
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Autowired SessionUserPrincipal sessionUserPrincipal;
#Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException
{
// find/get userprincipal code omitted
sessionUserPrincipal.setPrincipal(userprincipal);
}
}
Use session bean:
#Service
public class DefaultSomeService implements SomeService {
#Autowired private SessionUserPrincipal sessionUserPrincipal;
}

Related

How to manage scope session in spring boot?

I created a spring boot app with a controller layer, a service layer and a persistance layer. And i used spring security with JWT to provide the login process and to manage http sessions. I injected the service in the controller as follows :
#RestController
#ComponentScan(basePackages ={"com.example.service"})
#RequestMapping("/releve")
#CrossOrigin(origins = "http://192.168.1.13:4200")
public class ReleveController {
#Autowired
ReleveService releveService;
#Autowired
#Qualifier("authenticationManagerBean")
AuthenticationManager authenticationManager;
#Autowired
PasswordEncoder encoder;
#PreAuthorize("hasRole('ADMIN')")
#GetMapping(value="/listNiveaux")
public List<Niveau> listNiveaux() {
return releveService.listNiveaux();
}
#PreAuthorize("hasRole('ADMIN')")
#PostMapping(value="/modifNiveaux",consumes = "application/json")
public void modifNiveaux(#RequestBody Niveau niveau) {
releveService.modifNiveaux(niveau);
}
#PreAuthorize("hasRole('ADMIN')")
#PostMapping(value="/delNiveaux",consumes = "application/json")
public void delNiveaux(#RequestBody Niveau niveau) {
releveService.delNiveaux(niveau);
}
#PreAuthorize("hasRole('ADMIN')")
#PostMapping(value="/sauvegardeNiveau")
public void sauvegardeNiveau() {
releveService.sauvegardeNiveau();
}
}
Here are the annotations that i used in my service :
#Service(value = "ReleveService")
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
#EnableJpaRepositories(basePackages="com.example.releve.repository", entityManagerFactoryRef="entityManagerFactory")
public class ReleveServiceImpl implements ReleveService {
...
}
The problem is that despite the fact that the scope is session, the service ( or the bean ) is reinstanciated in every REST request : it does not get destroyed however, i used #PostConstruct and #PreDestroy to verify that it does not have the same behavior as the request scope.
How do i solve this ?
PS : i use Angular for the front-end app and there is another controller class for the login web service.

CDI - #Injected field null

I want to initialize a collection and fill it with data at the startup of my application. Then I would like to access it everywhere in my application. I want for example that my REST API can access the shared collection that is already populated with data.
I first tried to do it with an startup class annotated with #Startup and #Singleton. When I injected my userService in there I had some problems and because of the advice in this post: #Inject and #PostConstruct not working in singleton pattern I tried to do it with an #ApplicationScoped annotation:
#Named
#ApplicationScoped
public class KwetterApp {
#Inject
private UserService service;
#PostConstruct
public void init() {
try {
User harry = new User("Harry", "harry#outlook.com", "New York", "http://harry.com", "Hi, I'm Harry!", UserType.REGULAR);
User nick = new User("Nick", "nick#outlook.com", "California", "http://nick.com", "Hi, I'm Nick!", UserType.REGULAR);
User jane = new User("Jane", "jane#outlook.com", "Texas", "http://jane.com", "Hi, I'm Jane!", UserType.REGULAR);
Tweet tweet = new Tweet("eating...", harry);
Tweet tweet1 = new Tweet("swimming...", harry);
Tweet tweet2 = new Tweet("jogging...", jane);
harry.addTweet(tweet);
harry.addTweet(tweet1);
jane.addTweet(tweet2);
service.create(harry);
service.create(nick);
service.create(jane);
}
catch (Exception e){
e.printStackTrace();
}
}
public UserService getService() {
return service;
}
}
I inject this class in my rest service:
#RequestScoped
#Path("/user")
public class UserRest {
// #Inject
// private UserService userService;
#Inject
private KwetterApp kwetterApp;
// private KwetterApp kwetterApp = KwetterApp.getInstance();
private UserService userService = kwetterApp.getService();
#GET
#Produces({"application/json"})
public List<User> get() throws UserException {
return userService.getAll();
}
}
When injecting this KwetterApp it leads to the following exception:
StandardWrapperValve[rest.RestApplication]: Servlet.service() for servlet rest.RestApplication threw exception
java.lang.NullPointerException
at rest.UserRest.<init>(UserRest.java:27)
at rest.UserRest$Proxy$_$$_WeldClientProxy.<init>(Unknown Source)
I have an empty beans.xml file with bean-discovery-mode set to 'all'. The CDI framework should recognize my KwetterApp class for injection, right? Why is it null?
Thanks in advance,
Mike
Here
#Inject
private KwetterApp kwetterApp;
private UserService userService = kwetterApp.getService();
I do not think the kwetterApp field is going to be set before userService.
CDI will set that field after the object has been constructed.
An alternative, which should be used anyway, is constructor injection
#RequestScoped
#Path("/user")
public class UserRest {
private KwetterApp kwetterApp;
private UserService userService;
protected UserRest() {}
#Inject
public UserRest(final KwetterApp kwetterApp) {
this.kwetterApp = kwetterApp;
this.userService = kwetterApp.getService();
}
#GET
#Produces({"application/json"})
#Override
public List<User> get() throws UserException {
return userService.getAll();
}
}
A protected constructor is needed because #RequestScoped is a normal-scoped bean, and it must be proxiable, as described in the specification.
The only annotation that doesn't require an empty constructor is #Singleton (from javax.inject).
If you want to initialize an object by using an injected bean, then you have to use a #PostConstruct annotated method, because injected CDI beans are only available in CDI in a #PostContruct annotated method and afterwards and not during field initialization or the constructor invocation.
Therefore that the UserService is a CDI bean already, you can just inject it into your rest service bean, because it will be the same bean used within the current active scope. KwetterApp is available within the current active scope, so UserService will be as well. Only #Dependend scoped beans behave different, whereby each bean gets its own instance provided.

How to inject spring bean into ContainerRequestFilter using AutoWired?

I'm using RESTEasy 3 and Spring 4 and I'm trying to inject #Autowired an service bean into my interceptor as follow below:
But running this code it's returning Null Pointer Exception when access my access service:
#Provider
#MyAnnotationToIntercept
public class MyInterceptor implements ContainerRequestFilter {
private MyAccessService accessService;
#Autowired
public MyInterceptor(MyAccessService accessService) {
this.accessService = accessService;
}
public MyInterceptor() {
}
#Override
public void filter(ContainerRequestContext requestContext) {
// DO SOME STUFF Using accessService
}
}
#Component
public class MyAccessService {
private MyDep1 dep1;
#Autowired
public MyAccessService(Mydep1 dep1) {
this.dep1= dep1;
}
}
Is there any way to achieve this? It's really possible?
You will need to use WebApplicationContextUtils's method to get a bean inside filter which is not managed by spring. Here is the example
MyAccessService myAccessService = (MyAccessService) WebApplicationContextUtils.getRequiredWebApplicationContext(httpServletRequest .getServletContext()).getBean(MyAccessService.class);
And to get HttpServletRequest instance you can use #context injection
#Context
private HttpServletRequest httpServletRequest ;
Looks like you have placed #Autowired annotation at the wrong place. It should be above the declaration of accessService. And depending on how you have configured application context, you may/may not need a setter method for accessService instance variable.

Spring Jersey to inject ContainerRequestContext on request scoped class

I already looked at a lot of posts and nothing seems to work quite as i liked it to.
I want to inject an object into ContainerRequestContext properties from a filter and retrieve it later in other classes.
here is my filter:
#Priority(Priorities.AUTHENTICATION)
public class AuthorizationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
containerRequestContext.setProperty("myObject", new Object());
}
}
here is the class I want access to ContainerRequestContext:
#Provider
public class SessionContextProvider implements ISessionContextProvider {
#Context
private ContainerRequestContext request;
#Override
public Object getSessionContext() {
return request.getProperty("mySessionContext");
}
}
and my spring config:
#Bean(name="sessionContextProvider")
#Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public ISessionContextProvider sessionContextProvider() {
return new SessionContextProvider();
}
Everything works as expected if I inject ContainerRequestContext into my web resource. However if call my provider class ContainerRequestContext is always null.
I don't seem why this would not work.
Reagrds
Jonas
The problem is that with the Jersey/Spring integration, it allows us to successfully inject Spring beans into Jersey components, but this is not always true the other way around.
Jersey has it's own DI framework, HK21, and it is responsible for handle the injections with Jersey components. With the Jersey Spring integration, Jersey will lookup the Spring Bean, and take it as is, it won't inject it with any dependencies, I guess assuming Spring should take care of it's own injections.
That being said, if you don't require the ISessionContextProvider to be a Spring bean, then you can just make it an HK2 service. It's pretty simple. If you don't require any special initialization, you can just let HK2 create it. Here a simple configuration
public JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(new AbstractBinder() {
bind(SessionContextProvider.class)
.to(ISessionContextProvider.class)
.in(RequestScoped.class);
});
}
}
And that's it. You have an injectable ISessionContextProvider2.
If you require the ISessionContextProvider provider to be a Spring bean, then another option is to grab the bean from the Spring ApplicatinContext, and explicitly inject it yourself, using HK2's analogue of the ApplicationContext, its ServiceLocator. To do that we would need to use a Factory to do all the work transparently, so you can still inject the bean without doing any extra work on the outside
import javax.inject.Inject;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.ServiceLocator;
import org.springframework.context.ApplicationContext;
public class SessionContextProviderFactory
implements Factory<SessionContextProvider> {
private final ISessionContextProvider provider;
#Inject
public SessionContextProviderFactory(ApplicationContext ctx,
ServiceLocator locator) {
provider = ctx.getBean(ISessionContextProvider.class);
locator.inject(provider);
}
#Override
public ISessionContextProvider provide() {
return provider;
}
#Override
public void dispost(ISessionContextProvider provider) { /* noop */ }
}
Then just register the factory
public JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(new AbstractBinder() {
bindFactory(SessionContextProviderFactory.class)
.to(ISessionContextProvider.class)
.in(RequestScoped.class);
});
}
}
1 - hk2
2 - See also Dependency injection with Jersey 2.0
I found a workaround. I could inject the Spring HttpServletRequest into my AuthorizationFilter and set the SessionContext on this one.
#Autowired
private HttpServletRequest request;
....
request.setAttribute("mySessionContext", sessionContext);
And then because HttpServletRequest is known to spring and besically represents the same thing in my SessionContextProvider I do the same:
#Autowired
private HttpServletRequest request;
#Override
public SessionContext getSessionContext() {
return (SessionContext) request.getAttribute("mySessionContext");
}
I dont think this is the best solution possible. But it works. Ill wait for peeskillet for any other input if there is a better solution.
Regards
Jonas

CDI RequestScope giving multiple object on a request

I have an application in which we have a bean as
#RequestScoped
public class UserSession extends SessionMessage {
}
Then we have a WebFilter in which the above bean in injected. The filter populates all the properties on this userSession object.
public class SessionFilter implements Filter {
#Inject
private UserSession userSession;
.....
public void doFilter(....){
.....
someService.populateSession(userSession);
chain.doFilter(req, res)
}
}
and then on the
#Path("/token")
public class TokenService {
#Inject
private UserSession userSession;
.....
}
I am expecting the same object that is injected in the SessionFilter is injected in the TokenService. However, I am getting different object in TokenService than one injected in SessionFilter.
I am not able to figure out why same object is not injected when I have defined it to be RequestScope. Is there something I am missing?
What do you mean with the same object?
If you call the getter-methods, are the things set in the filter still there in TokenService?
The Beans injected in SessionFilter and TokenService are cdi-proxy-objects hiding the actual instance per request.
For further details see this question
I had the exact same Problem. I am Using Java EE 7 with wildfly 8.1.
My current solution is to use
HttpServletRequest context = (HttpServletRequest) PolicyContext.getContext("javax.servlet.http.HttpServletRequest");
this could be used inside your UserSession to populate the Session via Http-Header
#RequestScoped
public class UserSession extends SessionMessage {
public Session getSession() {
HttpServletRequest context = (HttpServletRequest) PolicyContext.getContext("javax.servlet.http.HttpServletRequest");
Session session = someService.populateSession(context);
return session;
}
}

Categories

Resources