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

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.

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.

Run Spring aspect advice on executing thread

I have a scenario in my springboot application, where I submit tasks into a threadpool for async execution.Now some of the methods inside child execution is part of aspect point advice with #AfterReturn.
I observe that even if processing is done asnyc, my main thread keeps executing the point cut advice from child thread and my service does not return a value until, all child thread finished execution.
Any pointer how to make the advice run on the executing thread itself?
So in short, controller method does not return response until dao method execution and its corresponding point cut is executed.
#Controller
#RequestMapping(value = "/api")
public class SampleController {
#Autowired
SampleService service;
#RequestMapping(value = "/action", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
#ResponseBody
public String action(#RequestBody String request){
service.action(request);
return "Success";
}
}
#Service
public class SampleService{
#Autowired
SampleDao dao;
#Async("threadPoolExecutor")
public void action(String request){
dao.action(request);
}
}
#Repository
public class SampleDao{
public void action(String request){
//do some db things
}
}
#Aspect
#Component
public class SampleAspect{
#AfterReturning(
pointcut = "execution( * com.sample.*.*.SampleDao.action(..))",
returning = "result")
public void audit(JoinPoint joinPoint, Object result) {
//dosome thing
}
}
The #Async on the service method does not mean that it will be submitted to the executor service and then return immediately, but that you can can have several incoming calls to the endpoint which will then be handled concurrently (this is the case per default anyway afaik, #Async is pretty much a marker only).
You can read this guide to see how this can be done properly.
The gist is that your service needs to create (and optionally return) some sort of Future (in the case of the post, CompletableFuture, as in
#Async
void serviceMethod(String request) {
CompletableFuture.submit(() -> dao.action(request));
}
It sounds like you do want to wait for the result to arrive though, so while this will work, I expect you'll run into problems later.

Spring Request scoped beans in lambda

I have a spring application that injects certain beans are injexted based on the request context. In this example it is the Facebook bean.
#RestController
#RequestMapping("facebook")
public class FacebookInjectionController {
#Autowired
private Facebook facebook;
#Autowired
private UserRepository userRepository;
#RequestMapping(method = RequestMethod.GET)
public List<String> blah() {
String firstName = facebook.userOperations().getUserProfile().getFirstName();
return Arrays.asList(firstName);
}
#RequestMapping(method = RequestMethod.GET, value = "complex")
public List<String> blah2() {
UserJwt principal = (UserJwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Stream<User> stream = StreamSupport.stream(userRepository.findAll().spliterator(), true);
return stream.filter(u -> u.getUid().equals(principal.getUid()))
.map(u ->
facebook.userOperations().getUserProfile().getFirstName()
).collect(Collectors.toList());
}
}
This code will run normally but every so often it will fail with the following error:
2017-02-09 01:39:59.133 ERROR 40802 --- [o-auto-1-exec-2]
o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for
servlet [dispatcherServlet] in context with path [] threw exception
[Request processing failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'scopedTarget.facebook': 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.] with root cause
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.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:41)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:340)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187)
at com.sun.proxy.$Proxy137.userOperations(Unknown Source)
at com.roomsync.FacebookInjectionController.lambda$blah2$5(FacebookInjectionController.java:43)
at com.roomsync.FacebookInjectionController$$Lambda$10/2024009478.apply(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:747)
at java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:721)
at java.util.stream.AbstractTask.compute(AbstractTask.java:316)
at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:902)
at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1689)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1644)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
I have tried multiple solutions (including Spring MVC: How to use a request-scoped bean inside a spawned thread?) but none have worked.
Is there a way to pass a request scoped bean down to a lambda or another thread?
going of what https://stackoverflow.com/users/1262865/john16384 said i have changed my config to:
#Bean
#Scope(value = "inheritableThreadScope", proxyMode = ScopedProxyMode.INTERFACES)
public ConnectionRepository connectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new IllegalStateException("Unable to get a ConnectionRepository: no user signed in");
}
return getUsersConnectionRepository(connectionFactoryLocator).createConnectionRepository(authentication.getName());
}
#Bean
#Scope(value="inheritableThreadScope", proxyMode=ScopedProxyMode.INTERFACES)
public Facebook facebook(ConnectionFactoryLocator connectionFactoryLocator) {
Connection<Facebook> connection = connectionRepository(connectionFactoryLocator).findPrimaryConnection(Facebook.class);
return connection != null ? connection.getApi() : null;
}
#Bean
#Scope(value = "inheritableThreadScope", proxyMode = ScopedProxyMode.INTERFACES)
public ExecutorService fbExecutor () {
return Executors.newSingleThreadExecutor();
}
the controller now looks like:
#RestController
#RequestMapping("facebook")
public class FacebookInjectionController {
#Autowired
private Facebook facebook;
#Autowired
private UserRepository userRepository;
#Autowired
private ExecutorService fbExecutor;
#RequestMapping(method = RequestMethod.GET)
public List<String> blah() {
String firstName = facebook.userOperations().getUserProfile().getFirstName();
return Arrays.asList(firstName);
}
#RequestMapping(method = RequestMethod.GET, value = "complex")
public List<String> blah2() throws ExecutionException, InterruptedException {
UserJwt principal = (UserJwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Stream<User> stream = StreamSupport.stream(userRepository.findAll().spliterator(), true);
Future<List<String>> submit = fbExecutor.submit(() -> stream.filter(u -> u.getUid().equals(principal.getUid()))
.map(u ->
facebook.userOperations().getUserProfile().getFirstName()
)
.collect(Collectors.toList()));
return submit.get();
}
}
i also have the following config:
#Configuration
public class BeanFactoryConfig implements BeanFactoryAware {
private static final Logger LOGGER = Logger.getLogger(BeanFactoryConfig.class);
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableBeanFactory) {
// logger.info("MainConfig is backed by a ConfigurableBeanFactory");
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
/*Notice:
*org.springframework.beans.factory.config.Scope
* !=
*org.springframework.context.annotation.Scope
*/
org.springframework.beans.factory.config.Scope simpleThreadScope = new SimpleThreadScope() {
#Override
public void registerDestructionCallback(String name, Runnable callback) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
attributes.registerDestructionCallback(name, callback, 3);
}
};
cbf.registerScope("inheritableThreadScope", simpleThreadScope);
/*why the following? Because "Spring Social" gets the HTTP request's username from
*SecurityContextHolder.getContext().getAuthentication() ... and this
*by default only has a ThreadLocal strategy...
*also see https://stackoverflow.com/a/3468965/923560
*/
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
else {
// logger.info("MainConfig is not backed by a ConfigurableBeanFactory");
}
}
}
even with this it sometimes get the error:
{
"timestamp": 1486686875535,
"status": 500,
"error": "Internal Server Error",
"exception": "java.util.concurrent.ExecutionException",
"message": "org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.facebook' defined in class path resource [com/roomsync/config/SocialConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.social.facebook.api.Facebook]: Factory method 'facebook' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.connectionRepository': Scope 'inheritableThreadScope' 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.",
"path": "/facebook/complex"
}
so it seems that im still missing the piece to activate the scope and copying the thread local context to it
There's two things going on:
1) Java streams use a common Fork/Join pool to execute things in parallel. These threads are not created by the Spring framework (or by you).
2) Request scoped beans are supported by using a ThreadLocal.
This means that if a thread, not created by Spring, tries to access a request scoped bean, it won't be found as the thread does not know about it (it is not in the ThreadLocal).
In order for you to resolve this issue you will need to take control of which threads are used for your streams. Once you achieved that, you can make a copy of the request scoped beans to use for the sub-threads. You'll also need to clean them up again after the thread has finished its task or you risk leaving beans behind that may be seen by the next task being executed on that thread.
To change which threads are used by parallel streams, see: Custom thread pool in Java 8 parallel stream
How to configure Spring properly to propagate request scoped beans to child threads you already found I think.
This is what worked for me to transfer request beans in fork-joined threads. The example is only for illustration.
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
// org.slf4j:slf4j-api:1.7.30
import org.slf4j.MDC;
// org.springframework:spring-web:5.2.12.RELEASE
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
class Scratch {
public static void main(String[] args) {
RequestAttributes context = RequestContextHolder.currentRequestAttributes();
Map<String, String> contextMap = MDC.getCopyOfContextMap();
List<String> list = new ArrayList<>();
list.parallelStream().map(id -> {
try {
// copy all required for spring beans
RequestContextHolder.setRequestAttributes(context);
MDC.setContextMap(contextMap);
// ************************************
// Spring request beans usage goes here
// ************************************
return 1;
} finally {
// clean all from thread local
MDC.clear();
RequestContextHolder.resetRequestAttributes();
}
})
.collect(Collectors.toList());
}
}
Is it required, that the stream is processed in parallel? That causes, that the lambda may be executed in another thread.
Stream stream = StreamSupport.stream(userRepository.findAll().spliterator(), false);
I had the same issue, I was trying to use the parallel stream to fetch job information from Kubernetes REST API since the parallel stream uses new Threads as John16384 explained, my code couldn't get the 'scopedTarget.oauth2ClientContext' because it's scope is request in Spring and the thread created by parallel stream couldn't access it. So I had to change it like below;
old version: items.parallelStream().map(jobItem -> createJobObject(jobItem, createJobTrigger(jobItem))).collect(Collectors.toList());
fixed version: items.stream().map(jobItem -> createJobObject(jobItem, createJobTrigger(jobItem))).collect(Collectors.toList());
and inside the createJobObject method, I was calling a REST service
restTemplate.getForEntity(url, KubernetesJob.class).getBody().getItems();

How to implement asynchronous rest webservice client using RestTemplate?

I want to invoke rest webservice call asynchronously using spring 3.1 rest template. Currently we are using Resttemplate.getForObject method to invoke the rest webservice. Now as per the requirement we have to invoke another webservice method concurrently along with existing one. We are invoking this method call from EJB 2.1 stateless session bean by instantiating the method class.
Sample code is as below:-
public class MyImplServerBean extends EJBObject{
RestServiceImpl restImpl = new RestServiceImpl();
ArrayList<User> userDetailsList = restImpl.getUserDetails(123);
}
public class RestServiceImpl {
RestTemplate template = new RestTemplate();
public ArrayList<User> getUserDetails(int userId){
ArrayList<User> userList = new ArrayList<User>();
String url = "http://localhost:7001/myUserService/userId";
User user = template.getForObject(url, User.class);
userList.add(user);
return userList;
}
We now need to make getUserDetails method call asynchronously. I got the idea using #Async annotation but not aware how to exactly implement.
Can you please help in this.
I used this tutorial to accomplish a similar task. I did some modifications. Here is how to get it working.
1) Create a configuration class and make sure it is scanned by ApplicationContext. I use AnnotationConfigWebApplicationContext.
#EnableAsync
#Configuration
public class AsyncConfiguration implements AsyncConfigurer
{
#Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
return executor;
}
#Override
public Executor getAsyncExecutor()
{
return new ThreadPoolTaskExecutor();
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler()
{
return new CustomAsyncExceptionHandler();
}
}
2) Annotate your method with
#Async("threadPoolTaskExecutor")
Your method must either return void or Future.
3) Make sure the async method is public and called from another class. This way Spring "sees" the annotation when the method is invoked reflectively.

How do I execute a method in Spring when the Server is ready?

I have the following problem: I'm using Spring-Boot for a little private web-based project and I want Spring to make a call to a webservice when it's started. And by started I mean "when my application is ready to handle requests".
I've already tried implementing the ApplicationListener<ContextRefreshedEvent> but it did not work, as the Event happend to early (i.e. before the embedded server was ready to handle request). Also the options mentioned in this question did not solve this problem.
My question now is: Is there any possibilty to tell Spring to execute something after the server has finished starting up and is ready to handle requests?
EDIT (in response to Daniel's answer):
The problem is that I need some injected properties to make that webservice call, and since injecting static values does not work in spring this approach is no option.
My listener, that does what I want, just a bit too early looks something like this:
#Component
public class StartupListener implements ApplicationListener{
#Autowired
private URLProvider urlProvider;
#Value("${server.port}")
private int port;
#Value("${project.name}")
private String projectName;
#Override
public final void onApplicationEvent(ContextRefreshedEvent event) {
RestTemplate template = new RestTemplate();
String url = uriProvider.getWebserviceUrl(this.projectName);
template.put(url, null);
}
}
SECOND EDIT:
Although this question solves a very similar problem it seems like I'm not able to inject into the object because it needs to have a constructor of the form (org.springframework.boot.SpringApplication, [Ljava.lang.String;).
Also it would be desirebale to solve it without having to create the spring.factories file but by using annotations.
If I understand what your problem is, you could call the webservice on your application main, right after it initiates.
public static void main(String[] args) {
new SpringApplication(Application.class).run(args);
//call the webservice for you to handle...
}
I'm not sure if this is what you want...
In your component you can use the #PostConstruct annotation. e.g.
#Component
public class StartupListener {
#Autowired
private URLProvider urlProvider;
#Value("${server.port}")
private int port;
#Value("${project.name}")
private String projectName;
#PostConstruct
public final void init() {
RestTemplate template = new RestTemplate();
String url = uriProvider.getWebserviceUrl(this.projectName);
template.put(url, null);
}
}
This will fire once the bean has been initialised and autowiring has taken place.
#Component
public class StartUp implements ApplicationListener<WebServerInitializedEvent> {
private WebClient webClient;
#Override
public void onApplicationEvent(WebServerInitializedEvent event) {
String baseUrl = "http://url.com"
webClient = WebClient.create(baseUrl);
executeRestCall(baseUrl+"/info");
}
void executeRestCall(String uri) {
try {
webClient.get()
.uri(uri)
.exchange()
.block();
} catch (Exception e) {
log.error("Request failed for url - {}",uri, e);
}
}}

Categories