I want to inject a #RequestScoped CDI bean in my Java EE 7 WebSocket endpoint.
However I am getting error WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped.
What am I doing wrong and why it is not possible?
#Named
#RequestScoped
public class Storage {
}
Which I #Inject in the endpoint like this:
#ServerEndpoint("/serverpush")
public class ContratoEndpoint {
#Inject
private Storage storage;
}
And I am getting the following stack trace:
org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
at org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:689)
at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:90)
at org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:165)
at org.jboss.weld.bean.ContextualInstance.getIfExists(ContextualInstance.java:63)
at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:83)
at org.jboss.weld.bean.proxy.ProxyMethodHandler.getInstance(ProxyMethodHandler.java:125)
As #John mentioned, RequestContext is not active in WebSocket methods. Instead of using Deltaspike (which is a good option), you can also write your own Interceptor to activate/deactivate weld RequestContext.
As you are using Wildfly, you can use weld as a provided dependency :
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-core</artifactId>
<version>2.2.12.Final</version>
<scope>provided</scope>
</dependency>
Then you can define an InterceptorBinding #RequestContextOperation :
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.TYPE })
public #interface RequestContextOperation
{
}
And the corresponding RequestContextInterceptor where we activate/deactivate the RequestContext:
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import org.jboss.weld.context.RequestContext;
import org.jboss.weld.context.unbound.Unbound;
#Interceptor
#RequestContextOperation
public class RequestContextInterceptor {
/** The RequestContext */
#Inject
#Unbound
private RequestContext m_requestContext;
/**
*
* #param p_invocationContext
* #return
* #throws Exception
*/
#AroundInvoke
public Object activateRequestContext(final InvocationContext p_invocationContext) throws Exception {
try {
m_requestContext.activate();
return p_invocationContext.proceed();
} finally {
m_requestContext.invalidate();
m_requestContext.deactivate();
}
}
}
You can then use the #RequestContextOperation annotation on your class or on a specific method :
#ServerEndpoint("/serverpush")
public class ContratoEndpoint {
#Inject
private Storage storage;
#OnMessage
#RequestContextOperation
public String handleMessage(String message){
// Here the #RequestScoped bean is valid thanks to the #RequestContextOperation InterceptorBinding
storage.yourMethod();
....
}
}
WebSockets do not initialize a request scope context for their method invocations. You can use deltaspike context control to manually start a request context for the method invocation.
Although Request Scope is not active in WebSockets (including Atmosphere) you can manually activate it using the approach proposed in a previous answer.
However, since CDI 2.0, you can use the #ActivateRequestContext annotation (from javax.enterprise.context.control.ActivateRequestContext) interceptor to do just that.
Either annotate the class or the method where you want to have Request Scope and this will activate it at the beginning of method execution and deactivate it when it finishes,
Related
I'm trying to print a message after the application startup with #PostConstruct, but nothing is printed.
package dev.renansouza.server;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
I have read that #PostConstruct is Lazy. Does this mean that I need to do
something else for this to work?
You can also use #EventListener annotation to acheive what you what, if using #PostConstruct is not that important to you.
For example in your case, you can add following code in any class to listen for application startup event.
#EventListener
void onStartup(ServerStartupEvent event) {
println("Hey, I work from anywhere in project..")
}
Code shared above is in Groovy
Keep in mind, the event listener added in main application class is usually called first from what I have observed.
The problem (aka feature) is, as you already mentioned, the lazy loading.
I see two solutions:
You have to do something to cause that bean to be initialized.
Change the scope of the bean from #Singleton to #Context
Micronaut has a few built-in scopes (see https://docs.micronaut.io/latest/guide/index.html#scopes) and the documentation of #Context states (see https://docs.micronaut.io/latest/api/io/micronaut/context/annotation/Context.html)
Context scope indicates that the classes life cycle is bound to that of the BeanContext and it should be initialized and shutdown during startup and shutdown of the underlying BeanContext.
Micronaut by default treats all Singleton bean definitions as lazy and will only load them on demand. By annotating a bean with #Context you can ensure that the bean is loaded at the same time as the context.
package dev.renansouza.server;
import javax.annotation.PostConstruct;
import io.micronaut.context.annotation.Context;
#Context
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
See the project at https://github.com/jeffbrown/renansouzapostconstruct.
https://github.com/jeffbrown/renansouzapostconstruct/blob/master/src/main/java/renansouzapostconstruct/ServerService.java
package renansouzapostconstruct;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
https://github.com/jeffbrown/renansouzapostconstruct/blob/master/src/main/java/renansouzapostconstruct/DemoController.java
package renansouzapostconstruct;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.HttpStatus;
#Controller("/demo")
public class DemoController {
private ServerService serverService;
public DemoController(ServerService serverService) {
this.serverService = serverService;
}
#Get("/")
public HttpStatus index() {
return HttpStatus.OK;
}
}
When you start the app you won't see the message printed to standard out because the service bean won't have been initialized. Send a request to http://localhost:8080/demo/ and then you will see the message printed to stdout.
I hope that helps.
I have a SqsQueueSender to send messages to AWS. I want to test this class. My thought is that it should be a #Component that is injected in to the classes that need it. Importantly, I want to configure the endpoint of the SqsQueueSender to be different in testing vs. production environments.
I've been moving #Autowired and #Component around the classes various different ways but must have some basic misunderstanding. Here's my latest configuration:
package com.foo.fulfillmentApi.dao;
import com.amazonaws.services.sqs.AmazonSQSAsyncClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.messaging.support.MessageBuilder;
#Component
public class SqsQueueSender {
private static final Logger LOGGER = LoggerFactory.getLogger(SqsQueueSender.class);
private final QueueMessagingTemplate queueMessagingTemplate;
#Autowired
AmazonSQSAsyncClient amazonSQSAsyncClient;
//This value is from /resources/application.properties
private #Value("${sqs.endpoint}") String endpoint;
public SqsQueueSender(AmazonSQSAsyncClient amazonSqsAsyncClient) {
amazonSqsAsyncClient.setEndpoint(endpoint);
this.queueMessagingTemplate = new QueueMessagingTemplate(amazonSqsAsyncClient);
}
public void send(String queueName, String message) {
this.queueMessagingTemplate.convertAndSend(queueName, MessageBuilder.withPayload(message).build());
}
}
The error message on startup states
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'com.amazonaws.services.sqs.AmazonSQSAsyncClient' available: expected
at least 1 bean which qualifies as autowire candidate. Dependency
annotations: {}
To implement SqsQueueSender you must pass an AmazonSQSAsyncClient. How do I make sure this component can access an existing bean of that type?
You need to create a configuration class. In your case it would be something like this:
#Configuration
public class AWSConfig {
#Bean(name ="awsClient")
public AmazonSQSAsync amazonSQSClient() {
AmazonSQSAsyncClient awsSQSAsyncClient
= new AmazonSQSAsyncClient();
// set up the client
return awsSQSAsyncClient;
}
If it has problems with injecting then add qualifier in qsQueueSender:
#Autowired
#Qualifier("awsClient")
AmazonSQSAsyncClient amazonSQSAsyncClient;
You can also do this using the xml configuration but as you are using annotations then this is more advisable approach.
Add #Component/#Service in com.amazonaws.services.sqs.AmazonSQSAsyncClient or return an object of that using #Bean annotation from configuration class.
If you use springboot - define into your startup application file as below
#Bean
public AmazonSNSAsync amazonSNSClient() {
ClientConfiguration config = new ClientConfiguration();
return AmazonSNSAsyncClient.asyncBuilder().withClientConfiguration(config)
.withRegion(Regions.fromName(region))
.withCredentials(new DefaultAWSCredentialsProviderChain())
.build();
}
What I have already working:
Spring version 4.1.4.RELEASE.
Jersey version 2.14.
I added maven dependency jersey-spring3 and excluded spring from it (spring-core, spring-web, spring-beans).
Spring components are scanned for - #ComponentScan.
"Controllers" are registered in Jersey's ResourceConfig...
... and are annotated with #Path and #Component...
... so that #Autowired beans fetch (#Transactional) POJOs from database...
... and Jersey with help of certain #Providers returns them in form of JSON.
What seems to be the problem is that a classes annotated with #Provider stop working as soon as I add annotation #Component.
Was anyone successful in combining those annotations? If yes, then what am I missing? If not, then it'll be quite clear that I have to move to alternative libraries. :)
While I think that using RestController could be the better way to go, this code (below) works - so my answer may be useful to everyone who is forced to use Jersey + Spring (for whatever reason...)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import javax.persistence.EntityNotFoundException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Provider
public class EntityNotFoundExceptionMapper implements ExceptionMapper<EntityNotFoundException> {
private final ApplicationContext applicationContext;
#Autowired
public EntityNotFoundExceptionMapper(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public Response toResponse(EntityNotFoundException exception) {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
You could use Spring's RestController, which was added in Spring 4.0. It allows you to use Autowired among other things.
#RestController
#RequestMapping("/msg")
public class MessageRestController {
#Autowired
private IShortMessageService shortMessageService;
#RequestMapping(value = "/message-json/{id}", method = RequestMethod.GET, produces = "application/json")
public ShortMessageDto getMessageJSONById(#PathVariable String id) {
Long id_value = null;
try {
id_value = Long.parseLong(id);
ShortMessageDto message = shortMessageService.getById(id_value);
if(message != null){
return message;
} catch (NumberFormatException e){
// log message
}
return null;
}
}
I have a complicated scenario, for which i have no idea how to go about:
I have my ejbs running in a remote server.
And my web application running in a different server.
I have an ApplicationContext, that will differ based on domain, language, country etc.
I would like this application context to be passed to the remote EJB anonymously, in such a way that developers dont have to invoke all of their backend requests with the ApplicationContext as a parameter.
This is the scenarion, lets says i have a remote Stateless EJB:
#Stateless
public class MyStateless implements MyStatelessRemote{
//The application context that needs to be supplied form the front-end
#Inject //probably define a producer method to always supply a new one.
private ApplicationContext applicationContext;
public void doCheckSomething(final MySomethingData data){}
}
And on the frontend:
#SessionScoped
#Named
public class MyController implements Serializable{
#EJB
private MyStatelessRemote statelessRemote
//The current application/session context to be passed to the Stateless ejb on every invocation.
#Inject
private ApplicationContext executionContext;
public void doSomeOrderOrSomethingSimilar(){
//At this point, the current application context needs to be supplied to the remote EJB
//Which it may use to check on order validity based on configurations such as country
//language etc.
statelessRemote.doCheckSomething(mySomething);
}
}
With more than 20 EJBS and each having an average of 8 to 10 methods, and considering the likelihood that almost every ejb may need to know the executioncontext of the caller,
is it possible to parse the current execution context, through configuration or otherwise to the ejb during invocation of any method?
I am using Wildfly8 with remote ejb3.1, CDI1.1, JSF2.2
The application context may change when, for example the user changes his/her language
EDIT:
I am looking for something similar to Web Service inbound and outbound interceptors.
What you're describing is not possible using CDI/EJB, without passing in parameters to your remote EJB.
After few months working on jboss/wildfly server, i finally found a away to achieve this functionality:
Client-Side Code: (Based on jboss ejbclient)
package com.mycompany.view.service.wildfly.invocationcontext;
import com.mycompany.ejb.internal.MyCompanyAccount;
import com.mycompany.view.service.account.LoggedInAccount;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import org.jboss.ejb.client.AttachmentKey;
import org.jboss.ejb.client.EJBClientContext;
import org.jboss.ejb.client.EJBClientInterceptor;
import org.jboss.ejb.client.EJBClientInvocationContext;
import static com.mycompany.management.wildfly.invocationcontext.MyCompanyInvocationContextKey.MYCOMPANY_ACCOUNT_NUMBER;
/**
* Registers itself as a jboss client interceptor.
* #author marembo
*/
#Singleton
#Startup
public class MyCompanyInvocationContextInterceptor implements EJBClientInterceptor {
private static final Logger LOG = Logger.getLogger(MyCompanyInvocationContextInterceptor.class.getName());
private static final AttachmentKey<Long> MYCOMPANY_ACCOUNT_NUMBER_KEY = new AttachmentKey<>();
#Inject
#LoggedInAccount
private Instance<MyCompanyAccount> loggedInAccount;
#PostConstruct
void registerSelf() {
EJBClientContext.requireCurrent().registerInterceptor(0, this);
}
#Override
public void handleInvocation(final EJBClientInvocationContext ejbcic) throws Exception {
LOG.log(Level.INFO, "Intercepting invocation on: {0}", ejbcic.getInvokedMethod());
final EJBClientContext clientContext = ejbcic.getClientContext();
if (!loggedInAccount.isUnsatisfied()) {
final MyCompanyAccount mycompanyAccount = loggedInAccount.get();
if (mycompanyAccount != null) {
final Long accountNumber = mycompanyAccount.getAccountNumber();
clientContext.putAttachment(MYCOMPANY_ACCOUNT_NUMBER_KEY, accountNumber);
}
}
ejbcic.getContextData().put(MYCOMPANY_ACCOUNT_NUMBER, "348347878483");
ejbcic.sendRequest();
}
#Override
public Object handleInvocationResult(final EJBClientInvocationContext ejbcic) throws Exception {
return ejbcic.getResult();
}
}
On the server side, i register a global interceptor:
package com.mycompany.management.wildfly.extension;
import com.mycompany.management.facade.account.MyCompanyAccountFacade;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.EJBContext;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
/**
* Default interceptor does not require #Interceptor
* #author marembo
*/
public class MyCompanyInvocationContextReceiver {
private static final Logger LOG = Logger.getLogger(MyCompanyInvocationContextReceiver.class.getName());
#Resource
private EJBContext ejbContext;
#EJB
private MyCompanyInvocationContext mycompanyInvocationContext;
#EJB
private MyCompanyAccountFacade mycompanyAccountFacade;
#AroundInvoke
public Object setMyCompanyAccount(final InvocationContext invocationContext) throws Exception {
final Map<String, Object> contextData = ejbContext.getContextData();
LOG.log(Level.INFO, "EJBContext data: {0}", contextData);
LOG.log(Level.INFO, "InvocationContext data: {0}", invocationContext.getContextData());
return invocationContext.proceed();
}
}
and the ejb-jar.xml:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns = "http://java.sun.com/xml/ns/javaee"
version = "3.1"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
<interceptors>
<interceptor>
<interceptor-class>com.mycompany.management.wildfly.extension.MyCompanyInvocationContextReceiver</interceptor-class>
</interceptor>
</interceptors>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>com.mycompany.management.wildfly.extension.MyCompanyInvocationContextReceiver</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>
As indicated on the server side interceptor, you can get the client-sent-data either from the InvocationContext or from the EJBContext.
I've got a session scoped CDI bean, and I need to somehow access the HttpServletRequest object in this bean's #PostConstruct method. Is it possible? I've tried to Inject such an object, but it results in:
WELD-001408 Unsatisfied dependencies for type [HttpServletRequest] with qualifiers [#Default] at injection point [[field] #Inject ...]
As I understood while googling, the Seam framework has such a functionality, but I have a standard Java EE application on a GlassFish server.
Is it even possible to somehow pass the request to a CDI bean's #PostConstruct method?
As per your comment, you want access to the user principal. You can just inject it like this: #Inject Principal principal; or #Resource Principal principal;, see Java EE 6 Tutorial.
Update
I'll answer your direct question. In Java EE 7 (CDI 1.1) injection of HttpServletRequest is supported out of the box. In Java EE 6 (CDI 1.0) however, this is not supported out of the box. To get it working, include the class below into your web-app:
import javax.enterprise.inject.Produces;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
#WebListener
public class CDIServletRequestProducingListener implements ServletRequestListener {
private static ThreadLocal<ServletRequest> SERVLET_REQUESTS = new ThreadLocal<>();
#Override
public void requestInitialized(ServletRequestEvent sre) {
SERVLET_REQUESTS.set(sre.getServletRequest());
}
#Override
public void requestDestroyed(ServletRequestEvent sre) {
SERVLET_REQUESTS.remove();
}
#Produces
private ServletRequest obtain() {
return SERVLET_REQUESTS.get();
}
}
Note: Tested only on GlassFish 3.1.2.2
When using the code from rdcrng be aware of the following:
* The producer-method obtain is dependent-scoped, thus is only called once for application scoped beans (and will resolve to problems for every other request except the first)
* You can solve this with #RequestScoped
* When RequestScoped annotated, you will only get a proxy, and thus you cannot cas it to HttpServletRequest. So you maybe want a producer for HttpServletRequest.
Also note: As per CDI specification link passage 3.6, java ee beans are NOT consideres managed beans. Thus you will end up with two instances of CDIServletRequestProducingListener - one managed by the Java EE container, one managed by the CDI-container. It only works because SERVLET_REQUESTS is static.
Following the modified code for your convenience.
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
#WebListener
public class CDIServletRequestProducingListener implements ServletRequestListener {
private static ThreadLocal<ServletRequest> SERVLET_REQUESTS = new ThreadLocal<ServletRequest>();
#Override
public void requestInitialized(ServletRequestEvent sre) {
SERVLET_REQUESTS.set(sre.getServletRequest());
}
#Override
public void requestDestroyed(ServletRequestEvent sre) {
SERVLET_REQUESTS.remove();
}
#RequestScoped
#Produces
private HttpServletRequest obtainHttp() {
ServletRequest servletRequest = SERVLET_REQUESTS.get();
if (servletRequest instanceof HttpServletRequest) {
return (HttpServletRequest) servletRequest;
} else {
throw new RuntimeException("There is no HttpServletRequest avaible for injection");
}
}
}