How to #Inject into a Runnable? - java

I have this JAX-RS resource
#Stateless
#Path("")
public class ServerResource {
#Inject
ServerService service;
#Resource
ManagedExecutorService mes;
#PUT
#Path("/prepare")
#Produces(MediaType.APPLICATION_JSON)
public void prepare(long id) {
var info = new Info();
info.setId(id);
service.saveInitialInfo(info);
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("/submit")
public void submit(long id, #Suspended AsyncResponse response) {
mes.execute(() -> {
var info = service.getInfo(id);
if (info == null) {
response.resume(new Exception("not found"));
}
info.doLongComputation();
response.resume(info);
});
}
And this service
#Stateless
public class ServerService {
#PersistenceContext
EntityManager entityManager;
public void saveInitialInfo(Info info) {
entityManager.persist(info);
}
public Info getInfo(long id) {
return entityManager.find(Info.class, id);
}
}
Info is an #Entity with some fields (and an #Id long id) and doLongComputation() manipulates these fields and takes time.
The client calls prepare once. This creates an Info and persists it. Now the client will perform calls to submit multiple times (with the same id). The problem is that the changes to the entity done in doLongComputation() are not "saved". On the next submit call, the entity will be in the same state that it was persisted at at first.
Because doLongComputation takes a long time I am using an AsyncResponse and executing the method inside its new thread. The problem seems to be with doing the operations inside mes.execute. If I change the method to
public void submit(long id, #Suspended AsyncResponse response) {
var info = service.getInfo(id);
if (info == null) {
response.resume(new Exception("not found"));
}
info.doLongComputation();
response.resume(info);
}
then subsequent calls to submit will actually see the changes the previous calls did.
How can I #Inject into the asynchronous response method?
I am using JavaEE 8. Iv'e seen Java EE 7 - Injection into Runnable/Callable object but that one is for JavaEE 7 and also the solution of using #Inject Instance<ServerService> service and then calling service.get() inside the Runnable does not help, probably because my classes are managed and there he creates one with new.

Related

IllegalStateException: No thread-bound request found - Spring Batch

I have to build a Spring Batch system. In my project I have to call, inside a batch scheduled, an Api method defined in Controller class of my project.
This is an example of controller
#RestController
public class MyController implements ExampleApi {
// other methods
#Override
public ResponseEntity<ExampleResponse> method(String object){
final ExampleResponse response = // execution
return ResponseEntity.status(HttpStatus.OK).body(response);
}
}
I call the method above in my CustomProcessor. This is an example of Batch processor
public class CustomProcessor implements ItemProcessor<List<String>, List<String>> {
#Autowired
private ExampleApi exampleApi;
#Override
public List<String> process(#NonNull List<String> objects) throws Exception {
objects.forEach(object -> exampleApi.method(object));
return objects;
}
}
When batch starts running I can call the api, but when I call inside another api in another class annotated like this:
#Component
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ObjectFactoryImpl implements ObjectFactory {
#Autowired
#Qualifier("anotherService")
private ObjectProvider<AnotherApi> anotherApiObjectProvider;
public Object find(Long objectId) {
Object object;
try {
object = anotherApiObjectProvider.getObject().getObjectById(objectId).getObject();
} catch (Exception e) {
throw new Exception....
}
}
I get this exception:
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.
I'm quite new to Spring Batch.

Reactive Mailer - blocking operation on a IO thread

With reactive mailer I am trying to persist if the email was succeeded or not.
Here down the code snippet that is not woking:
#Path("atendimentos")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class AtendimentoResource {
#Inject
AtendimentoHistoricoRepository atendimentoHistoricoRepository;
#Inject
ReactiveMailer mailer;
#GET
public Response findAll(#QueryParam("nome") String nome) {
AtendimentoHistorico atendimentoHistorico = new AtendimentoHistorico();
mailer.send(email).subscribe().with(success -> {
atendimentoHistorico.setEmailEnviado(true);
atendimentoHistoricoRepository.persist(atendimentoHistorico);
}, error -> {
});
}
}
Here is the thrown exception:
You have attempted to perform a blocking operation on a IO thread. This is not allowed, as blocking the IO thread will cause major performance issues with your application. If you want to perform blocking EntityManager operations make sure you are doing it from a worker thread.
If you want to block, you should use io.quarkus.mailer.Mailer instead of ReactiveMailer.
#Path("atendimentos")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class AtendimentoResource {
#Inject
AtendimentoHistoricoRepository atendimentoHistoricoRepository;
#Inject
Mailer mailer;
#GET
public Response findAll(#QueryParam("nome") String nome) {
AtendimentoHistorico atendimentoHistorico = new AtendimentoHistorico();
mailer.send(email);
atendimentoHistorico.setEmailEnviado(true);
atendimentoHistoricoRepository.persist(atendimentoHistorico);
}
}

Creating and injecting a per request scoped variable

I would like to have a variable that follows along the full lifecycle of a request in java EE.
For example it could be for a logging function, so that I can filter all log entries by request.
The key part that I want to get at is that it must be relatively easy to implement in an already existing application so if possible some sort of dependency injection that gets the variable related to the specific request.
I've tried injectiong a #RequestScoped variable, but it doesn't work since it is only scoped to the container. I would need to be able to inject the same object to different containers. Is this at all possible?
EDIT: I want something along the lines of this:
#RequestScoped
public class RequestVariables {
public String id;
}
#Stateless
public class Logger {
#Inject
private RequestVariables requestVariables;
public void log(String message) {
System.out.println(requestVariables.id + ":" + message);
}
}
#Stateless
public class Service {
#Inject
private Logger logger;
#Inject
private RequestVariables requestVariables;
public void save(String data) {
logger.log("Save");
session.save(data + requestVariables.id); //Maybe add request parameter to save aswell
}
}
public class API {
#Inject
private Service service;
#Inject
private Logger logger;
#Inject
private RequestVariables requestVariables;
#Path("/1")
#GET
public Response get(#QueryParam("data") String data) {
requestVariables.id = UUID.randomUUID().toString()
service.save(data);
logger.log("Get");
return Response.status(204).build();
}
}
Currently this is what I have experimented with:
#RequestScoped
public class RequestScope {
private int test = 0;
public RequestScope(int test) {
this.test = test;
}
public RequestScope(){}
public int getTest() {
return test;
}
public void setTest(int test) {
this.test = test;
}
}
#Provider
public class RequestScopeFilter implements ContainerRequestFilter {
#Inject
private javax.inject.Provider<RequestScope> requestScopeProvider;
#Context
private HttpServletRequest request;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestScopeProvider.get().setTest(42);
request.setAttribute("test", "superTest");
}
}
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
#TransactionAttribute(value=TransactionAttributeType.NOT_SUPPORTED)
public class Service {
#Context
private HttpServletRequest httpServletRequest;
#Inject
private Provider<RequestScope> requestScopeProvider;
public void test() {
RequestScope scope = requestScopeProvider.get();
String test = (String)httpServletRequest.getAttribute("test");
}
}
So when I get the scope from my service then it is a new object with test set to 0, and then it throws an NPE since httpServletRequest is null
option #1
Implement an Interceptor and set the request id as HttpServletRequest attribute:
#AroundInvoke
public Object setRequestId(InvocationContext ic) throws Exception {
HttpServletRequest request = [..] // getHttpServletRequest(ic);
request.setAttribute("request-id", UUID.randomUUID().toString());
return ic.proceed();
}
Then use HttpServletRequest everywhere you need it
#Context
private HttpServletRequest httpRequest;
option #2
If want just to filter your logs by an unique id, you can configure your Logger to print the thread name: [%t]
Example: Log4j PatternLayout
option #3
Use a custom java bean to encapsulate the request data (query param, request id etc.) and pass this bean across your application services.
public class API {
#Inject
private Service service;
#Path("/1")
#GET
public Response get(MyCustomRequestBean data) {
service.doSomejob(data);
return Response.status(204).build();
}
}
Set the request id and query param in ParamConverter:
Jax-RS ParamConverter - ParamConverterProvider method return type mismatch
You can inject a provider in your service:
#Inject
Provider<RequestVariables> vars
And then call get () to get the instance. If you try to get () in a thread outside a request scope context you'll get an exception. I would however try to structure in a way that would not allow this to happen
A solution that I found is to use ThreadLocal variables. It seems rather dirty, but it works since each request is executed on it's own thread(as far as I am aware). So this is what I got:
public class RequestScope {
private static final ThreadLocal<String> id = ThreadLocal.withInitial(() -> UUID.randomUUID().toString());
public static String get() {
return id.get();
}
}
With that I can also easily exchange the ThreadLocal to return something more specific if so desired.
And I can get the variables from pretty much anywhere, assuming that the request is not starting a different thread

Spring #Async not allowing use of autowired beans [duplicate]

This question already has an answer here:
Accessing HttpSession outside of the originally receiving thread
(1 answer)
Closed 6 years ago.
I have a fully-annotation-driven Spring Boot 1.3.5 app which has this asynchronous service which needs to autowire another service bean (And in the future it will need to autowire a repository bean, but I'm not there yet) in order to perform some business logic:
#Service
public class AsyncService {
#Autowired
public HelpingService helpingService;
#Async
public Future<String> doFoo(String someArgument)
throws InterruptedException {
Thread.sleep(3000);
System.out.println("about to do Foo "+someArgument);
String result = "";
try {
result = helpingService.getSomeStuff(someArgument);
}
catch (Exception e) {
e.printStackTrace();
}
return new AsyncResult<String>(hello);
}
}
That method above is being called from a #Controller bean, which has other endpoints (Non-async) that work as expected also using this
#Controller
public class MyController extends BaseController {
#Autowired
HelpingService helpingService;
#Autowired
AsyncService asyncService;
#RequestMapping(method=RequestMethod.GET, value={"/rest/threads/getIp/{jobId}"}, produces={"application/json"})
public ResponseEntity<?> getLog(#PathVariable("jobId") String jobId) throws InterruptedException {
asyncService.doFoo(jobId);
return new ResponseEntity<>(HttpStatus.OK);
}
}
And here's helpingService's implementation (It's an interface), calling any method works perfectly fine when I'm not doing it from the #Async method above:
#Service
#Validated
public class HelpingServiceImpl implements HelpingService {
#Autowired
HttpSession httpSession;
#Value(value="${projName}")
private String projName;
public String getServerAddress(){
AuthRegion region = (AuthRegion) httpSession.getAttribute("region");
if (region != null)
return region.getServerAddress();
else
return null;
}
#Override
public String getSomeStuff(String jobId) {
String responseString = "";
String projName = this.projName;
String serverAddress = getServerAddress(); // Code stops here with an exception
// Some code here that works fine outside this thread
return responseString;
}
}
This is the exception being caught:
about to do Foo (267)
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.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:309)
at org.springframework.web.context.support.WebApplicationContextUtils.access$400(WebApplicationContextUtils.java:64)
at org.springframework.web.context.support.WebApplicationContextUtils$SessionObjectFactory.getObject(WebApplicationContextUtils.java:366)
at org.springframework.web.context.support.WebApplicationContextUtils$SessionObjectFactory.getObject(WebApplicationContextUtils.java:361)
at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:307)
at com.sun.proxy.$Proxy96.getAttribute(Unknown Source)
at corp.fernandopcg.myapp.service.ThreadServiceImpl.getRundeckServerPort(ThreadServiceImpl.java:45)
at corp.fernandopcg.myapp.service.ThreadServiceImpl.getJobExecutionOutput(ThreadServiceImpl.java:65)
at corp.fernandopcg.myapp.service.AsyncService.doFoo(AsyncService.java:40)
at corp.fernandopcg.myapp.service.AsyncService$$FastClassBySpringCGLIB$$7e164220.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:115)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
I added (With some changes as I couldn't extend AsyncConfigurer at the same time as SpringBootServletInitializer, and I had to catch an exception not mentiones there) the taskExecutor part to my Application main class as follows, guided by this tutorial which does look similar to what I need, in my opinion
#SpringBootApplication
#EnableAsync
#EnableJpaRepositories(repositoryFactoryBeanClass = DataTablesRepositoryFactoryBean.class)
public class MyApplication extends SpringBootServletInitializer implements AsyncConfigurer{
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("SomeRandomLookup-");
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
// TODO Auto-generated method stub
return null;
}
}
Can I tell my #Async service to be able to use other services of the application? Because if that's not possible, I don't really see the use of these threading mechanism.
This is a great illustration of why request-scope injection can be problematic. Your HelpingServiceImpl has a hidden dependency on the request-specific HttpSession, which looks like a field but is actually a proxy that is resolved by Spring on each call to always refer to the "current" request (using a thread-local variable).
The problem is that by making your call #Async, you're separating the HelpingServiceImpl invocation from the request that triggered it, and there's no longer the implicit connection of being on the same thread that would allow it to pull information from the globalish context.
The most straightforward fix is to make your dependencies explicit--instead of having your HelpingServiceImpl grab the region directly off of the HttpSession, pass the region to it as a method parameter.

ContextNotActiveException while calling #Asynchronous method of #Stateless bean

I am injecting a #Stateless bean in a Asynchronous Servlet and calling #Asynchronous method from the Serrvlet. In the server logs of the jboss i am not able to see any of the Exception but whhile starting the Java Mission Control ,Flight Recorder i can see ContextNotActiveExcetion whenever Servlet makes a call to the #Asyncrhonous method.
Servlet ::
#WebServlet(urlPatterns = { "/asyncservice" }, asyncSupported = true)
public class AsyncServiceServlet extends HttpServlet {
#Inject
private Service service;
protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
final AsyncContext asyncContext = request.startAsync(request, response);
asyncContext.start(new Runnable() {
#Override
public void run() {
try {
service.service(asyncContext);
} catch (ContextNotActiveException | IOException e) {
e.printStackTrace();
}
});
}
Service class ::
#Stateless
public class Service {
#Asynchronous
public void service(final AsyncContext asyncContext) throws IOException {
HttpServletResponse res = (HttpServletResponse) asyncContext.getResponse();
res.setStatus(200);
asyncContext.complete();
}
}
the stack trace i can see in the flight Recorder ::
java.lang.Throwable.<init>() 4
java.lang.Exception.<init>() 4
java.lang.RuntimeException.<init>() 4
javax.enterprise.context.ContextException.<init>() 4
javax.enterprise.context.ContextNotActiveException.<init>() 4
org.jboss.weld.context.ContextNotActiveException.<init>(Enum,Object[]) 4
org.jboss.weld.manager.BeanManagerImpl.getContext(Class) 4
org.jboss.as.weld.ejb.EjbRequestScopeActivationInterceptor.processInvocation(InterceptorContext) 4
org.jboss.invocation.InterceptorContext.proceed() 4
org.jboss.invocation.InitialInterceptor.processInvocation(InterceptorContext) 4
org.jboss.invocation.InterceptorContext.proceed() 4
org.jboss.invocation.ChainedInterceptor.processInvocation(InterceptorContext) 4
org.jboss.as.ee.component.interceptors.ComponentDispatcherInterceptor.processInvocation(InterceptorContext) 4
org.jboss.invocation.InterceptorContext.proceed() 4
org.jboss.as.ejb3.component.pool.PooledInstanceInterceptor.processInvocation(InterceptorContext) 4
org.jboss.invocation.InterceptorContext.proceed() 4
org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInOurTx(InterceptorContext,TransactionManager,EJBComponent) 4
org.jboss.as.ejb3.tx.CMTTxInterceptor.required(InterceptorContext,EJBComponent,int) 4
org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(InterceptorContext)
I have been going through many posts but still the issue remain the same, please help me out .
javadoc for AsyncContext.start:
Registers the given AsyncListener with the most recent asynchronous
cycle that was started by a call to one of the
ServletRequest.startAsync() methods. The given AsyncListener will
receive an AsyncEvent when the asynchronous cycle completes
successfully, times out, or results in an error.
Implying that by the time this call to
service.service(asyncContext);
Is made, the httpservletrequest "context" may not be available and the request may even have been committed, resulting to CDI not being able to determine any '#RequestScoped' beans used by your service.
Note that AsyncContext.start registers an onEvent to be invoked when the async call is completed, or on error, not when it starts.
You would probably want to add listeners to be invoked before calling AsyncContext.start
The exception has no effect on functionality; it's handled under the hood.
The ContextNotActiveExcetion applies to #RequestScoped beans. You start double async processing with AsyncContext.start and the #Asynchronous EJB call.
The exception you see within flight recorder is to test, whether the default RequestScoped context is active and to proceed, if so. If the RequestScoped context is not active, a new EjbRequestContext is activated and associated with the thread.
You can cause a visible ContextNotActiveExcetion when you create a #SessionScoped bean and inject/access that one within your Service
MySessionScoped.java
#SessionScoped
public class MySessionScoped implements Serializable {
private int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
Service.java
#Stateless
public class Service {
#Inject
private MySessionScoped mySessionScoped;
#Asynchronous
public void service(final AsyncContext asyncContext) throws IOException {
System.out.println(mySessionScoped.getValue());
HttpServletResponse res = (HttpServletResponse) asyncContext.getResponse();
res.setStatus(200);
asyncContext.complete();
}
}

Categories