How to #Autowire a RequestScoped bean only in web requests - java

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;
}
}

Related

CDI Conditionally produce/override a bean if a JNDI resource is present

I'm trying to find a way to conditionally produce an #Alternative/#Specialized bean in CDI 1.2 if a given JNDI resource is present.
My use case is that I have a default bean which uses an in-memory data structure. However, if a given JNDI resource is present, I want to use that resource instead in a different implementation of my service. My problem is that as soon as I use a #Resource(lookup='jndiName') annotation, Weld throws an exception if the resource is not found.
I wanted to use a producer to conditionally create the resource-based bean, but if I try to inject the #Resource and it is missing, Weld fails.
For example:
interface MyService{
void doSomething();
}
// In memory implementation that I always want to have available for injection if no other MyService bean implementation is available
#ApplicationScoped
public class InMemory implements MyService{
Map<String, String> persistence = new HashMap<>();
public void doSomething(){ persistence.put("now", new Date());}
}
// Bean to be available IFF a JNDI based cache is found
public class JndiPersistence implements MyService{
Cache<String, String> persistence;
public JndiPersistence(Cache persistence){ this.persistence = persistence);}
public void doSomething(){ persistence.put("now", new Date());}
}
// client class which uses the service
public class DataManager(){
private MyService myservice;
#Inject
public DataManager( MyService myservice ){ this.myservice = myservice; }
// calls the injected service bean
public void manageMyData(){
myservice.doSomething();
}
Finally, the producer:
public class JndiProducer{
#Resource(lookup="java:comp/env/persistenceCache")
Cache cache;
#Produces
#ApplicationScoped
public MyService jndiBean(){
return new JndiPersistence( cache );
}
}
I have tried changing my Resource injection to Instance<Cache> but if the jndi name is missing, it still throws an exception and doesn't even make it to the Producer. Finally, I am not sure how to make the entire Producer conditional on the resource being present and override the initial bean.
What is the correct approach for this in CDI? Is this even feasible, or is there a better approach to use instead?
The way I would go is a conditional producer with manual JNDI interactions.
I would start by making sure CDI ignores both implementations of MyService:
#Vetoed
public class InMemory implements MyService { ... }
#Vetoed
public class JndiPersistence implements MyService { ... }
Then go ahead with the producer (please note code is approximate, just to show the general principle, it might require adjustments/fine tuning):
// no real need for scoping the producer, unless the environment/configuration demands it
public class MyServiceProducer {
private MyService instance;
#Produces
#ApplicationScoped // this scoping applies to the produced bean
public MyService getMyService() {
// I *think* CDI guarantees that this will not be accessed concurrently,
// so no synchronization needed
if (instance == null) {
try {
InitialContext ic = new InitialContext();
Cache cache = (Cache) ic.lookup("java:comp/env/persistenceCache");
instance = new JndiPersistence(cache);
} catch (NamingException ne) {
// you can probably ignore it...
// use correct judgement though: you may want to log it
// or even fail altogether (i.e. rethrow it) e.g. if you sense
// that JNDI should be there but it is malfunctioning
instance = new InMemory();
}
}
return instance;
}
}

Can't achieve dependency injection oustide a controller in Spring Booot

I am new at spring MVC framework and i am currently working in a web application that uses a session scoped bean to control some data flow.
I can access these beans in my application context using #Autowired annotation without any problem in the controllers. The problem comes when I use a class in service layer that does not have any request mapping (#RequestMapping, #GetMapping nor #PostMapping) annotation.
When I try to access the application context directly or using #Autowired or even the #Resource annotation the bean has a null value.
I have a configuration class as follow:
#Configuration
#EnableAspectJAutoProxy
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class, basePackages = "com.quantumx.nitididea.NITIDideaweb.repository")
public class AppConfig implements WebMvcConfigurer {
#Bean (name = "lastTemplate")
#SessionScope
public LastTemplate getlastTemplate() {
return new LastTemplate();
}
//Some extra code
}
The POJO class is defined as :
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I have a Test class that is annotated as service and does not have any request mapping annotated method:
//#Controller
#Service
public class Test {
// #Autowired
// private ApplicationContext context;
// #Autowired
#Resource(name = "lastTemplate")
public LastTemplate lastTemplate;
// #Autowired
// public void setLastTemplate(LastTemplate lastTemplate) {
// this.lastTemplate = lastTemplate;
// }
public Test() {
}
// #RequestMapping("/test")
public String testing() {
// TemplateForma last = (TemplateForma) context.getBean("lastInsertedTemplate");
// System.out.println(last);
System.out.println(lastTemplate);
// System.out.println(context.containsBean("lastTemplate"));
// System.out.println(context.getBean("lastTemplate"));
System.out.println("Testing complete");
return "Exit from testing method";
// return "/Messages/Success";
}
}
As you can see, there is a lot of commented code to show all the ways i have been trying to access my application context, using an Application context dependency, autowiring, declaring a resource and trying with a request mapping. The bean is null if no controller annotation and request mapping method is used and throws a java null pointer exception when I use the context getBean() methods.
Finally I just test my class in a controller that i have in my app:
#RequestMapping("/all")
public String showAll(Model model) {
Test test = new Test();
test.testing();
return "/Administrator/test";
}
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked. How can access my application context in a service class without mapping a request via controller?
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked
It should have worked in this case.
How can access my application context in a service class without mapping a request via controller?
Try one of these :-
#Autowired private ApplicationContext appContext;
OR
Implement ApplicationContextAware interface in the class where you want to access it.
Edit:
If you still want to access ApplicationContext from non spring managed class. Here is the link to article which shows how it can be achieved.
This page gives an example to get spring application context object with in non spring managed classes as well
What worked for me is that session scoped bean had to be removed in the application configuration declaration and moved to the POJO definition as follows:
#Component
#SessionScope
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I just call the bean using #Autowired annotation.

Is it possible to inject bean in request scope if possible and if it is not active for thread, then inject prototype?

I want to inject request scoped bean into singleton scoped. I can do it using Provider. The problem occurs when no scope request is not active for a thread. In such case I would like to inject bean in e.g. prototype scope. Is that possible?
E.g. code:
public class Tenant {
private String name;
...
}
#Configuration
public class AppConfig {
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_REQUEST)
public Tenant prototypeBean() {
return new Tenant();
}
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Tenant prototypeBean() {
return new Tenant();
}
#Bean
public MySingletonBean singletonBean() {
return new MySingletonBean();
}
public class MySingletonBean {
#Autowired
private Provider<Tenant> beanProvider;
//inject conditionally on request or prototype scope
public void showMessage() {
Tenant bean = beanProvider.get();
}
}
I want to avoid error with this message:
Error creating bean with name 'tenantDetails':
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.
If something like this is required, then the design of an application should be reconsidered.
Is it possible? Yes.
#Component
#RequestScope
public class RequestScopeTenant implements Tenant {
private String name;
...
}
This way we have request scoped bean.
#Component
#PrototypeScope
public class DefaultTenant implements Tenant {
private String name;
...
}
here we have our default Tenant.
#Configuration
public class AppConfig {
#Primary
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Tenant prototypeBean(RequestScopeTenant requestScopeTenant, DefaultTenant defaultTenant) {
return RequestContextHolder.getRequestAttributes() != null ? requestScopeTenant : defaultTenant;
}
#Bean
public MySingletonBean singletonBean() {
return new MySingletonBean();
}
This way when no request scope is available default tenant is returned.
And again, if you would have to implement something like this - change the design or create custom scope.

How to configure a Bean prototype scoped provider to use session information on Bean creation?

I need to provide beans based on session information every time the proxy is acessed to obtain the instance. How I can do that?
For now I tried the following. For example:
The first class defines a session scoped bean.
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class BeanSession implements Serializable {
private Serializable rootState;
public <T extends Serializable> T getRootState() {
return (T) rootState;
}
public void setRootState(Serializable rootState) {
this.rootState = rootState;
}
}
The second class has some logic related to their domain and also knows how to provide the information. The bean must be a created every time because the information can change during the thread processing. Therefore, every time the Attribute1 is acessed I will be sure to get the bean with fresh information.
#Service
public class Attribute1Service {
#Resource
private BeanSession beanSession;
public void setDefaultValue() {
Configuration configuration = beanSession.getRootState();
configuration.getAttribute1().setValue("VALUE 1");
}
#Bean
public Attribute1 attribute1() {
Configuration configuration = beanSession.getRootState();
return configuration.getAttribute1();
}
}
Finally, the third class declares the attribute1 as dependency to execute their own logic.
#Service
public class Attribute2Service {
#Resource
private BeanSession beanSession;
#Resource
private Processor processor;
#Resource
private Attribute1 attribute1;
public void defineAttribute2() {
Configuration configuration = beanSession.getRootState();
String value = processor.getValue(configuration, attribute1);
configuration.getAttribute2().setValue(value);
}
public void defineAttribute3() {
Configuration configuration = beanSession.getRootState();
String value = processor.getValue(configuration, attribute1);
configuration.getAttribute3().setValue(value);
}
}
However, the problem is that during attribute1 creation I am getting the following error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'Attribute2Service': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'attribute1' defined in class path resource [Attribute1Service.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [String]: Factory method 'attribute1' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.beanSession': Scope 'session' 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: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:324) ~[spring-context-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1378) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
...
I don't want to access attribute1 information from beanSession on Attribute2Service because it will create a hard coupling between the information providers and consumers.
The exception says it all - your attribute1 bean gets created during app initialization (via session scoped bean) but there is no thread bound with request. You should proxy your attribute1 bean as well because you are injecting it to singleton (attribute 2 service. )
Based on the insights given by Alexander.Furer. I created my own scope and managed it to call the bean provider to have the fresh bean in every access of Attribute1 methods.
To this, I extended the following Scope:
// Register scope as "runtime"
public class RuntimeScope implements Scope {
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
return objectFactory.getObject();
}
...
}
The new Attribute1 service:
#Service
public class Attribute1Service {
#Resource
private BeanSession beanSession;
public void setDefaultValue() {
Configuration configuration = beanSession.getRootState();
configuration.getAttribute1().setValue("VALUE 1");
}
#Bean
#Scope(value = "runtime", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Attribute1 attribute1() {
Configuration configuration = beanSession.getRootState();
return configuration.getAttribute1();
}
}
The consumer Attribute2service:
#Service
public class Attribute2Service {
#Resource
private BeanSession beanSession;
#Resource
private Processor processor;
#Resource
private Attribute1 attribute1;
public void defineAttribute2() {
Configuration configuration = beanSession.getRootState();
String value = processor.getValue(configuration, attribute1.getValue()); // Will call Attribute1 service to require the fresh bean
configuration.getAttribute2().setValue(value);
}
public void defineAttribute3() {
Configuration configuration = beanSession.getRootState();
String value = processor.getValue(configuration, attribute1.getValue()); // Will call Attribute1 service to require the fresh bean
configuration.getAttribute3().setValue(value);
}
}
The problem I wasn't seeing is that Attribute1 should be a proxy to handle the bean instantiation. Therefore, creating my own scope I can guarantee that accesses to attribute1 (made by Attribute2Service with attribute1.getValue()) methods will create a fresh bean (provided by Attribute1Service).

how to use an Object injected with #Inject within a server endpoints #OnOpen method

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

Categories

Resources