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;
}
}
Related
Lately, I've come to know that RequestScoped beans are not usable outside a web transaction.
The problem is that outside the web transaction I do not want to use that bean, instead of having an error.
How could I achieve this ?
The component using the request scoped bean :
#Component
public class JavaComponent {
#Autowired
private RequestScopedBean requestScopedBean;
#Override
public void doStuff() {
// TODO if in web transaction, use RequestScopedBean , otherwhise don't
}
}
The bean:
#Component
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {
public String getInfo() {
return "information about the web transaction";
}
}
EDIT: the error I get when trying to use the JavaComponent outside the web request is :
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'JavaComponent.requestScopedBean': Scope
'request' is not active for the current thread; consider defining a
scoped proxy for this bean if you intend to refer to it from a
singleton; nested exception is 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.
The way I use the bean while outside the web request thread is by having #Async methods running in separated threads.
Autowire ApplicationContext only and lookup your request scoped bean, then perform a null check.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
#Service
public class TestService {
#Autowired
private ApplicationContext applicationContext;
public void myMethod() {
RequestScopedBean bean = applicationContext.getBean(RequestScopedBean.class);
if (bean != null) {
// do stuff
}
}
}
I've come up with a better solution because applicationContext.getBean() throws an exception when called, instead, it's better to check if the current thread is executed in a web request context and then get the bean.
I've also tested the performances and the get bean is very fast ( 0ms ) probably because the request scoped bean pretty light
/**
* The Class AuditConfig.
*/
#Component
public class AuditConfig implements AuditorAware<String> {
/**
* The Constant SYSTEM_ACCOUNT.
*/
public static final String SYSTEM_ACCOUNT = "system";
#Autowired
private ApplicationContext context;
/**
* Gets the current auditor.
*
* #return the current auditor
*/
#Override
public Optional<String> getCurrentAuditor() {
return Optional.ofNullable(getAlternativeUser());
}
private String getAlternativeUser() {
try {
// thread scoped context has this != null
if (RequestContextHolder.getRequestAttributes() != null) {
Object userBean = context.getBean(AuditRequestScopedBean.BEAN_NAME);
if (StringUtils.isNotBlank(((AuditRequestScopedBean) userBean).getUser()))
{
return ((AuditRequestScopedBean) userBean).getUser();
}
}
} catch (Exception e) {
return SYSTEM_ACCOUNT;
}
return SYSTEM_ACCOUNT;
}
}
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;
}
Let's say I have a bean:
#Component
#RequestScope
class MyBean {
private final HttpServletRequest httpServletRequest;
#Autowired
public MyBean(HttpServletRequest httpServletRequest) {
this.httpServletRequest = httpServletRequest;
}
public Map<String, String> getProperties() { .. }
}
In the above bean, getProperties does not use httpServletRequest at all, and I need to call the method in the bootstrapping phase, which is outside of a request context. Doing so, obviously gives me the error No thread-bound request found .... Is there a way to ask spring to make the value null if not in a request context?
I have a Bean, which creates an instance of an object, which i need to inject into other beans. Everything works fine, i can print a property of the injected object in the #PostConstruct method, but if i try to call a method from that injected object inside the #OnOpen method of a ServerEndpoint it gives me a NullPointerException.
Here is my ServerEndpoint
#Named
#ApplicationScoped
#ServerEndpoint(value = "/websocket")
public class BeanThree {
private String message = "test";
#Inject private User user;
#PostConstruct
public void init() { System.out.println(user.getUserName()); } <-- displayed in the console correctly
public String getMessage() { return this.message; }
#OnOpen
public void onOpen(Session session) {
System.out.println("onOpen");
System.out.println(user.getUserName()); <-- causes NullPointerException
}
}
Is it possible to fix this?
Edit1:
Im using cdi 1.2, jetty 9.1, jsf 2.2, java-ee7 and websockets from java-ee7
The problem is that BeanThree is declared both a CDI bean and an endpoint at the same time.
It has to be split into two different beans:
#ServerEndpoint("/endpoint")
public class BeanThree {
#Inject
ApplicationScopedBean bean;
#OnOpen
public String onOpen(Session s) { System.out.println(bean); }
#OnMessage
public String onMessage(String message) { System.out.println(bean); }
}
#ApplicationScoped
public class ApplicationScopedBean { ... }
But there's another issue.
CDI / Websocket integration is very limited: out of the box you can inject #ApplicationScoped and probably #Dependent beans only.
From your snippet it seems you intend to use #SessionScoped User bean withing a Websocket session. That's not going to work because Websocket and HTTP sessions differ.
You'll have to manage Websocket sessions and session-bound data by yourself. Here's an example.
One way to do this is to allow CDI to instantiate it as a CDI bean.
Then subclasses the following classes: ServerEndpointConfig.Configurator
#ServerEndpoint(..., configurator=MyCustomConfigurator.class)
#SessionScoped
#Named("myMessageHandler")
public class MyMessageHandler{
#Inject
private MyInjectable instance;
...
}
public class MyCustomConfigurator extends ServerEndpointConfig.Configurator{
public <T extends Object> getEndpointInstance(Class<T> endpointClass) throws InstantiationException{
//do cdi lookup for endpoint using the simple name.
}
To have a reference to the cdi BeanManager, have a look at this thread:
http://dominikdorn.com/2010/04/cdi-weld-manual-bean-lookup/
In your case, you dont have a reference to FacesContext, so use the ServletContext
I'm running GWT app on Jetty 6.1 with Weld 2.0.
Got the next code:
#SessionScoped
public class SessionContext implements Serializable {
#Inject
private HttpSession httpSession;
public SessionContext() {
super();
//at this point httpSession is null
}
}
What am I missing, why HttpSession is not injected? Reference says that Injecting the HttpSession will force the session to be created.
Change the definition of
public SessionContext() {
super();
//at this point httpSession is null
}
to
public SessionContext(HttpSession httpSession) {
super();
this.httpSession = httpSession;
//check session here
}
Also use constructor injection
Otherwise provide a setter method for httpSession
It's better to use #PostConstruct to annotate some other method, here you have :
Initializing a managed bean specifies the lifecycle callback method
that the CDI framework should call after dependency injection but
before the class is put into service.
This is exactly the place where your injections are done but no code has been invoked.
like this :
#PostConstruct
public void doMyStuff() {
//feel free to access your injections here they are not null!
}