0

I am calling another microservice once my current microservice is up and ready using feign client in my current microservice built using Jhipster. So my Feign Interface is

package com.persistent.integration.client;

import java.util.List;

import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.persistent.integration.service.dto.DataPipelineDTO;

@AuthorizedFeignClient(name = "Integrationconfiguration") 
public interface DataPipelinesResourceFeign {
     @RequestMapping(value = "/api/data-pipelines", method = RequestMethod.GET)     
     List<DataPipelineDTO> getAllDataPipelines(@RequestParam(value = "pageable") Pageable pageable );       
}

}

And I have implemented ApplicationRunner where I have called feign client method.

@Component
public class ApplicationInitializer implements ApplicationRunner {

    @Autowired
    private DataPipelinesResourceFeign dataPipelinesResourceFeign; 

    @Autowired
    private ActiveMQListener activeMqListener;


    @Override
    public void run(ApplicationArguments args) throws Exception {
        // TODO Auto-generated method stub
        Pageable pageable = PageRequest.of(0, 20);
        try {
        List <DataPipelineDTO> allStartedDataPipeLines = dataPipelinesResourceFeign.getAllDataPipelines(pageable);      //.stream().filter(p->p.getState().equals(State.STARTED)).collect(Collectors.toList());

        allStartedDataPipeLines.forEach(datapipe -> 
                {
                    try {
                        activeMqListener.consume(datapipe);
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                });
        } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
        }
    }

But after running this, it gives below exception at dataPipelinesResourceFeign.getAllDataPipelines :

com.netflix.hystrix.exception.HystrixRuntimeException: DataPipelinesResourceFeign#getAllDataPipelines(Pageable) failed and no fallback available.
    at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:819)
    at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:804)
    at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140)
    at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
    at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
    at com.netflix.hystrix.AbstractCommand$DeprecatedOnFallbackHookApplication$1.onError(AbstractCommand.java:1472)

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.oauth2ClientContext': 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. at

org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(Abstrac>tBeanFactory.java:362) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractB>eanFactory.java:199) at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTarge>tSource.java:35) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.>java:193) at com.sun.proxy.$Proxy147.getAccessToken(Unknown Source) at com.persistent.integration.security.oauth2.AuthorizationHeaderUtil.getAuthoriza>tionHeaderFromOAuth2Context(AuthorizationHeaderUtil.java:28) at com.persistent.integration.client.TokenRelayRequestInterceptor.apply(TokenRelay>RequestInterceptor.java:23) at feign.SynchronousMethodHandler.targetRequest(SynchronousMethodHandler.java:158) at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:88) at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108) at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302) at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ... 68 more Caused by: 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.currentRequestAttr>ibutes(RequestContextHolder.java:131) at org.springframework.web.context.request.AbstractRequestAttributesScope.get(Abst>ractRequestAttributesScope.java:42) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(Abstrac>tBeanFactory.java:350)

many suggestions on internet were to add listerner RequestContextListener. But problem persisted even if I added listener in webConfigurer.java in onStartup method.

{

servletContext.addListener(RequestContextListener.class);

} But of no use.

Any leads would be appreciated.

Philippe Warnon
  • 49
  • 1
  • 10
user3388006
  • 1
  • 2
  • 2

2 Answers2

0

I found a workaround for this. I don't know why TokenRelayRequestIntercepton isn't working but you can use your own RequestInterceptor based on Spring's SecurityContext.

First, define a RequestInterceptor :

    public class MyRequestInterceptor implements RequestInterceptor {

    public static final String AUTHORIZATION = "Authorization";
    public static final String BEARER = "Bearer";

    public MyRequestInterceptor() {
        super();
    }

    @Override
    public void apply(RequestTemplate template) {
        // demander un token à keycloak et le joindre à la request
        Optional<String> header = getAuthorizationHeader();
        if (header.isPresent()) {
            template.header(AUTHORIZATION, header.get());
        }
    }

    public static Optional<String> getAuthorizationHeader() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication  != null && authentication.getDetails() != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
            OAuth2AuthenticationDetails oAuth2AuthenticationDetails =
                (OAuth2AuthenticationDetails) authentication.getDetails();

            return Optional.of(String.format("%s %s", oAuth2AuthenticationDetails.getTokenType(),
                oAuth2AuthenticationDetails.getTokenValue()));

        } else {
            return Optional.empty();
        }
    }

}

and then, declare a config class for your feign client using your RequestInterceptor, it should contains something like this :

@Bean(name = "myRequestInterceptor")
public RequestInterceptor getMyRequestInterceptor() throws IOException {
    return new MyRequestInterceptor();
}

Your Feign client shoud look like this:

 @FeignClient(name = "SERVICE_NAME", configuration = MyFeignConfiguration.class)
public interface MyRestClient {
Philippe Warnon
  • 49
  • 1
  • 10
0

I had the same issue with Feign Client running on startup using ApplicationRunner and I came up with following solution.

I defined my FeignClientsConfiguration with OAuth2FeignRequestInterceptor, which accepts predefined bean DefaultOAuth2ClientContext and OAuth2 configuration OAuth2ProtectedResourceDetails:

@Configuration
public class MyConfig extends FeignClientsConfiguration {

    @Bean
    public RequestInterceptor oauth2FeignRequestInterceptor(                                                           DefaultOAuth2ClientContext oAuth2ClientContext, MyOauth2Properties properties) {
        return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resourceDetails(properties));
    }

    @Bean
    public DefaultOAuth2ClientContext oAuth2ClientContext() {
        return new DefaultOAuth2ClientContext();
    }

    private OAuth2ProtectedResourceDetails resourceDetails(MyOauth2Properties oauth2Properties) {
        ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
        resourceDetails.setAccessTokenUri(oauth2Properties.getAccessTokenUri());
        resourceDetails.setUsername(oauth2Properties.getUsername());
        resourceDetails.setPassword(oauth2Properties.getPassword());
        resourceDetails.setClientId(oauth2Properties.getClientId());
        return resourceDetails;
    }
}

Your feign client will look something like this:

@FeignClient(url = "http://localhost:8080/api/v1")
public interface FeignClient {
}

After all this, calling FeignClient from ApplicationRunner.run() works fine. Spring Boot 2.2.6