2

I'm trying to write an integration test for a sink I'm creating for Spring XD. Here is my ModuleConfiguration.java

@Configuration
@EnableIntegration
public class ModuleConfiguration {

    private OAuth2AccessToken token;

    @Bean
    public MessageChannel input() {
        return new DirectChannel();
    }

    @Bean
    protected OAuth2ProtectedResourceDetails resource() {
        BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails();
        resource.setAccessTokenUri("https://xxxxxxxxxxx");
        resource.setClientId("id");
        resource.setClientSecret("secret");
        resource.setGrantType("authorization_code");
        return resource;
    }

    @Bean
    public OAuth2RestTemplate oAuthTemplate() {
        AccessTokenRequest atr = new DefaultAccessTokenRequest();
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(atr));
        return restTemplate;
    }

    @Bean
    public HeaderMapper<HttpHeaders> headerMapper() {
        HeaderMapper<HttpHeaders> mapper = new DefaultHttpHeaderMapper();
        HttpHeaders headers = new HttpHeaders();
        token = oAuthTemplate().getAccessToken();
        String authHeader = "Bearer " + token.getValue();

        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", authHeader);
        mapper.toHeaders(headers);

        return mapper;
    }

    @ServiceActivator(inputChannel="inputChannel")
    @Bean
    public MessageHandler httpout() {
        HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("https://yyyyyyyyy");
        handler.setCharset("UTF-8");
        handler.setHttpMethod(HttpMethod.POST);
        handler.setHeaderMapper(headerMapper());
        return handler;
    }
}

I implemented the test the same way that a test for a processor is written, and tell spring xd that this module is a processor because that's what it expects. Not sure, but I don't think that matters, the exception I'm getting has to do with OAuth. Here is SinkTest.java

public class SinkTest {

    private static SingleNodeApplication application;

    private static int RECEIVE_TIMEOUT = 5000;

    private static String moduleName = "oauth-sink";

    @BeforeClass
    public static void setUp() {
        application = new SingleNodeApplication().run();
        SingleNodeIntegrationTestSupport singleNodeIntegrationTestSupport = new SingleNodeIntegrationTestSupport(
                application);
        singleNodeIntegrationTestSupport
                .addModuleRegistry(new SingletonModuleRegistry(ModuleType.processor, moduleName));
    }

    @Test
    public void test() {
        String streamName = "sinkTest";
        byte[] encoded = null;
        String data = null;
        try {
            encoded = Files.readAllBytes(Paths.get("src/test/resources/dataunit.json"));
            data = new String(encoded, "utf-8");
        } catch (IOException e) {
            e.printStackTrace();
            fail("Exception thrown");
        }

        String processingChainUnderTest = moduleName;
        SingleNodeProcessingChain chain = chain(application, streamName, processingChainUnderTest);
        chain.sendPayload(data);
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> result = (List<Map<String, Object>>) chain.receivePayload(RECEIVE_TIMEOUT);

        // Unbind the source and sink channels from the message bus
        chain.destroy();
        // an exception is thrown by zookeeper when the spring xd container shuts down after the test passes
    }
}

I get an exception on this line of my ModuleConfiguration token = oAuthTemplate().getAccessToken();

Caused by: error="access_denied", error_description="Unable to obtain a new access token for resource 'null'. The provider manager is not configured to support it."
    at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:146)
    at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:118)
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)

If I use client_credentials for the GrantType in ModuleConfiguration like this

@Bean
    protected OAuth2ProtectedResourceDetails resource() {
        BaseOAuth2ProtectedResourceDetails resource = new ClientCredentialsResourceDetails();
        resource.setAccessTokenUri("https://xxxxxxx");
        resource.setClientId("id");
        resource.setClientSecret("secret");
        resource.setGrantType("client_credentials");
        return resource;
    }

I get

Caused by: org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:107)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97)
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
    ... 29 more

What is the correct grant type to use when I just have clientId and secret? Is there something missing from my ModuleConfiguration?

Update I changed the inputChannel in @ServiceActivator to the correct value and I am now getting the same exception as before with BaseOAuth2ProtectedResourceDetails. Now getting a different exception with ClientCredentialsResourceDetails

Caused by: java.lang.IllegalArgumentException: Map has no value for 'object-type'
    at org.springframework.web.util.UriComponents$MapTemplateVariables.getValue(UriComponents.java:276)
    at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:221)
    at org.springframework.web.util.HierarchicalUriComponents$FullPathComponent.expand(HierarchicalUriComponents.java:650)
    at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:319)
    at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:46)
    at org.springframework.web.util.UriComponents.expand(UriComponents.java:152)
    at org.springframework.web.util.UriComponentsBuilder.buildAndExpand(UriComponentsBuilder.java:376)
    at org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:376)
    ... 35 more
gary69
  • 3,620
  • 6
  • 36
  • 50

1 Answers1

1

First of all try to inject OAuthRestTemplate into HttpExecutionMessageHandler.

And don't use that headerMapper at all, even like a bean. Seems for me that is exactly why we have the OAuth RestTemplate extension.

Another your problem is around the wrong channel for the sink and its service-activator. Right, that must be the input bean, but use the same name from @ServiceActivator. That is a root of cause of your Dispatcher has no subscribers.

@ServiceActivator(inputChannel="inputChannel")
@Bean
public MessageHandler httpout() {
    HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("https://yyyyyyyyy", oAuthTemplate());
    handler.setCharset("UTF-8");
    handler.setHttpMethod(HttpMethod.POST);
    return handler;
}

If you have some variables in the URL, like https://foo.bar/{foo}/{bar}, you have to provide setUriVariableExpressions(Map<String, Expression> uriVariableExpressions) to resolve them at runtime against requestMessage: http://docs.spring.io/spring-integration/reference/html/http.html#_mapping_uri_variables

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Will fix formatting when reach my PC. Isn't easy from the phone – Artem Bilan Apr 18 '16 at 01:50
  • There doesn't appear to be a setter in `HttpRequestExecutingMessageHandler` for a `RestTemplate`. The closest one looks like `setRequestFactory(ClientHttpRequestFactory)` in which case I'd need to create an object of this type, maybe I can inject a `RestTemplate` into that. If I don't user a `HeaderMapper` with the handler how do I insert the headers? `RestTemplate` doesn't have a setter for headers. Thank you for the `@ServiceActivator` fix that fixed my other exception, updating the question. – gary69 Apr 18 '16 at 15:40
  • ??? `public HttpRequestExecutingMessageHandler(String uri, RestTemplate restTemplate) {` – Artem Bilan Apr 18 '16 at 15:45
  • The ` Map has no value for 'object-type'` is another problem. It smells like a new SO question :-). – Artem Bilan Apr 18 '16 at 15:48
  • Thank you, after using the constructor above I am now getting ` Map has no value for 'object-type'`` for both `BaseOAuth2ProtectedResourceDetails` subclasses and grant types. I'll do some research on this new exception. – gary69 Apr 18 '16 at 15:51
  • I forgot to include the 'object-type' url parameter when I redacted the url, thanks for catching that – gary69 Apr 18 '16 at 16:00