org.springframework.cloud.netflix.turbine.stream.TurbineStreamTests.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.cloud.netflix.turbine.stream.TurbineStreamTests.java

Source

/*
 * Copyright 2013-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.netflix.turbine.stream;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.stubrunner.StubTrigger;
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.integration.support.management.MessageChannelMetrics;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * @author Spencer Gibb
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TurbineStreamTests.Application.class, webEnvironment = WebEnvironment.NONE, value = {
        "turbine.stream.port=0", "spring.jmx.enabled=true", "spring.main.web-application-type=servlet",
        // TODO: we don't need this if we harmonize the turbine and hystrix destinations
        // https://github.com/spring-cloud/spring-cloud-netflix/issues/1948
        "spring.cloud.stream.bindings.turbineStreamInput.destination=hystrixStreamOutput",
        "spring.jmx.enabled=true", "stubrunner.workOffline=true",
        "stubrunner.ids=org.springframework.cloud:spring-cloud-netflix-hystrix-stream:${projectVersion:2.0.0.BUILD-SNAPSHOT}:stubs" })
@AutoConfigureStubRunner
public class TurbineStreamTests {

    private static Log log = LogFactory.getLog(TurbineStreamTests.class);

    @Autowired
    StubTrigger stubTrigger;

    @Autowired
    ObjectMapper mapper;

    @Autowired
    @Qualifier(TurbineStreamClient.INPUT)
    SubscribableChannel input;

    RestTemplate rest = new RestTemplate();

    @Autowired
    TurbineStreamConfiguration turbine;

    private CountDownLatch latch = new CountDownLatch(1);

    @EnableAutoConfiguration
    @EnableTurbineStream
    public static class Application {
    }

    @Test
    @Ignore // FIXME 2.0.0 Elmurst stream missing class @Controller?
    public void contextLoads() throws Exception {
        rest.getInterceptors().add(new NonClosingInterceptor());
        int count = ((MessageChannelMetrics) input).getSendCount();
        ResponseEntity<String> response = rest.execute(
                new URI("http://localhost:" + turbine.getTurbinePort() + "/"), HttpMethod.GET, null, this::extract);
        assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.TEXT_EVENT_STREAM);
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        Map<String, Object> metrics = extractMetrics(response.getBody());
        assertThat(metrics).containsEntry("type", "HystrixCommand");
        assertThat(((MessageChannelMetrics) input).getSendCount()).isEqualTo(count + 1);
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> extractMetrics(String body) throws Exception {
        String[] split = body.split("data:");
        for (String value : split) {
            if (value.contains("Ping") || value.length() == 0) {
                continue;
            } else {
                return mapper.readValue(value, Map.class);
            }
        }
        return null;
    }

    private ResponseEntity<String> extract(ClientHttpResponse response) throws IOException {
        // The message has to be sent after the endpoint is activated, so this is a
        // convenient place to put it
        stubTrigger.trigger("metrics");
        byte[] bytes = new byte[1024];
        StringBuilder builder = new StringBuilder();
        int read = 0;
        while (read >= 0 && StringUtils.countOccurrencesOf(builder.toString(), "\n") < 2) {
            read = response.getBody().read(bytes, 0, bytes.length);
            if (read > 0) {
                latch.countDown();
                builder.append(new String(bytes, 0, read));
            }
            log.debug("Building: " + builder);
        }
        log.debug("Done: " + builder);
        return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders())
                .body(builder.toString());
    }

    /**
     * Special interceptor that prevents the response from being closed and allows us to
     * assert on the contents of an event stream.
     */
    private class NonClosingInterceptor implements ClientHttpRequestInterceptor {

        private class NonClosingResponse implements ClientHttpResponse {

            private ClientHttpResponse delegate;

            public NonClosingResponse(ClientHttpResponse delegate) {
                this.delegate = delegate;
            }

            @Override
            public InputStream getBody() throws IOException {
                return delegate.getBody();
            }

            @Override
            public HttpHeaders getHeaders() {
                return delegate.getHeaders();
            }

            @Override
            public HttpStatus getStatusCode() throws IOException {
                return delegate.getStatusCode();
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return delegate.getRawStatusCode();
            }

            @Override
            public String getStatusText() throws IOException {
                return delegate.getStatusText();
            }

            @Override
            public void close() {
            }

        }

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
                throws IOException {
            return new NonClosingResponse(execution.execute(request, body));
        }

    }
}