com.netflix.ribbon.RibbonTest.java Source code

Java tutorial

Introduction

Here is the source code for com.netflix.ribbon.RibbonTest.java

Source

/*
 * Copyright 2014 Netflix, Inc.
 *
 * 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 com.netflix.ribbon;

import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.MockWebServer;
import com.netflix.hystrix.HystrixExecutableInfo;
import com.netflix.hystrix.exception.HystrixBadRequestException;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import com.netflix.ribbon.http.HttpRequestTemplate;
import com.netflix.ribbon.http.HttpResourceGroup;
import com.netflix.ribbon.hystrix.FallbackHandler;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.reactivex.netty.protocol.http.client.HttpClientResponse;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.junit.BeforeClass;
import org.junit.Test;
import rx.Observable;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func1;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import static org.junit.Assert.*;

public class RibbonTest {

    private static String toStringBlocking(RibbonRequest<ByteBuf> request) {
        return request.toObservable().map(new Func1<ByteBuf, String>() {
            @Override
            public String call(ByteBuf t1) {
                return t1.toString(Charset.defaultCharset());
            }

        }).toBlocking().single();
    }

    @BeforeClass
    public static void init() {
        LogManager.getRootLogger().setLevel(Level.DEBUG);
    }

    @Test
    public void testCommand() throws IOException, InterruptedException, ExecutionException {
        MockWebServer server = new MockWebServer();
        String content = "Hello world";
        MockResponse response = new MockResponse().setResponseCode(200).setHeader("Content-type", "text/plain")
                .setBody(content);

        server.enqueue(response);
        server.enqueue(response);
        server.enqueue(response);
        server.play();

        HttpResourceGroup group = Ribbon.createHttpResourceGroup("myclient",
                ClientOptions.create().withMaxAutoRetriesNextServer(3).withReadTimeout(300000)
                        .withConfigurationBasedServerList(
                                "localhost:12345, localhost:10092, localhost:" + server.getPort()));
        HttpRequestTemplate<ByteBuf> template = group.newTemplateBuilder("test", ByteBuf.class).withUriTemplate("/")
                .withMethod("GET").build();

        RibbonRequest<ByteBuf> request = template.requestBuilder().build();

        String result = request.execute().toString(Charset.defaultCharset());
        assertEquals(content, result);
        // repeat the same request
        ByteBuf raw = request.execute();
        result = raw.toString(Charset.defaultCharset());
        raw.release();
        assertEquals(content, result);

        result = request.queue().get().toString(Charset.defaultCharset());
        assertEquals(content, result);
    }

    @Test
    public void testHystrixCache() throws IOException {
        // LogManager.getRootLogger().setLevel((Level)Level.DEBUG);
        MockWebServer server = new MockWebServer();
        String content = "Hello world";
        MockResponse response = new MockResponse().setResponseCode(200).setHeader("Content-type", "text/plain")
                .setBody(content);
        server.enqueue(response);

        server.enqueue(response);
        server.play();

        HttpResourceGroup group = Ribbon.createHttpResourceGroupBuilder("myclient").build();
        HttpRequestTemplate<ByteBuf> template = group.newTemplateBuilder("test", ByteBuf.class)
                .withUriTemplate("http://localhost:" + server.getPort()).withMethod("GET")
                .withRequestCacheKey("xyz").build();
        RibbonRequest<ByteBuf> request = template.requestBuilder().build();
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        try {
            RibbonResponse<ByteBuf> ribbonResponse = request.withMetadata().execute();
            assertFalse(ribbonResponse.getHystrixInfo().isResponseFromCache());
            ribbonResponse = request.withMetadata().execute();
            assertTrue(ribbonResponse.getHystrixInfo().isResponseFromCache());
        } finally {
            context.shutdown();
        }
    }

    @Test
    public void testCommandWithMetaData() throws IOException, InterruptedException, ExecutionException {
        // LogManager.getRootLogger().setLevel((Level)Level.DEBUG);
        MockWebServer server = new MockWebServer();
        String content = "Hello world";
        for (int i = 0; i < 6; i++) {
            server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-type", "text/plain")
                    .setBody(content));
        }
        server.play();

        HttpResourceGroup group = Ribbon.createHttpResourceGroup("myclient", ClientOptions.create()
                .withConfigurationBasedServerList("localhost:" + server.getPort()).withMaxAutoRetriesNextServer(3));

        HttpRequestTemplate<ByteBuf> template = group.newTemplateBuilder("test").withUriTemplate("/")
                .withMethod("GET").withCacheProvider("somekey", new CacheProvider<ByteBuf>() {
                    @Override
                    public Observable<ByteBuf> get(String key, Map<String, Object> vars) {
                        return Observable.error(new Exception("Cache miss"));
                    }
                }).build();
        RibbonRequest<ByteBuf> request = template.requestBuilder().build();
        final AtomicBoolean success = new AtomicBoolean(false);
        RequestWithMetaData<ByteBuf> metaRequest = request.withMetadata();
        Observable<String> result = metaRequest.toObservable()
                .flatMap(new Func1<RibbonResponse<Observable<ByteBuf>>, Observable<String>>() {
                    @Override
                    public Observable<String> call(final RibbonResponse<Observable<ByteBuf>> response) {
                        success.set(response.getHystrixInfo().isSuccessfulExecution());
                        return response.content().map(new Func1<ByteBuf, String>() {
                            @Override
                            public String call(ByteBuf t1) {
                                return t1.toString(Charset.defaultCharset());
                            }
                        });
                    }
                });
        String s = result.toBlocking().single();
        assertEquals(content, s);
        assertTrue(success.get());

        Future<RibbonResponse<ByteBuf>> future = metaRequest.queue();
        RibbonResponse<ByteBuf> response = future.get();
        assertTrue(future.isDone());
        assertEquals(content, response.content().toString(Charset.defaultCharset()));
        assertTrue(response.getHystrixInfo().isSuccessfulExecution());

        RibbonResponse<ByteBuf> result1 = metaRequest.execute();
        assertEquals(content, result1.content().toString(Charset.defaultCharset()));
        assertTrue(result1.getHystrixInfo().isSuccessfulExecution());
    }

    @Test
    public void testValidator() throws IOException, InterruptedException {
        // LogManager.getRootLogger().setLevel((Level)Level.DEBUG);
        MockWebServer server = new MockWebServer();
        String content = "Hello world";
        server.enqueue(
                new MockResponse().setResponseCode(200).setHeader("Content-type", "text/plain").setBody(content));
        server.play();

        HttpResourceGroup group = Ribbon.createHttpResourceGroup("myclient",
                ClientOptions.create().withConfigurationBasedServerList("localhost:" + server.getPort()));

        HttpRequestTemplate<ByteBuf> template = group.newTemplateBuilder("test", ByteBuf.class).withUriTemplate("/")
                .withMethod("GET").withResponseValidator(new ResponseValidator<HttpClientResponse<ByteBuf>>() {
                    @Override
                    public void validate(HttpClientResponse<ByteBuf> t1) throws UnsuccessfulResponseException {
                        throw new UnsuccessfulResponseException("error", new IllegalArgumentException());
                    }
                }).build();
        RibbonRequest<ByteBuf> request = template.requestBuilder().build();
        final CountDownLatch latch = new CountDownLatch(1);
        final AtomicReference<Throwable> error = new AtomicReference<Throwable>();
        request.toObservable().subscribe(new Action1<ByteBuf>() {
            @Override
            public void call(ByteBuf t1) {
            }
        }, new Action1<Throwable>() {
            @Override
            public void call(Throwable t1) {
                error.set(t1);
                latch.countDown();
            }
        }, new Action0() {
            @Override
            public void call() {
            }
        });
        latch.await();
        assertTrue(error.get() instanceof HystrixBadRequestException);
        assertTrue(error.get().getCause() instanceof UnsuccessfulResponseException);
    }

    @Test
    public void testFallback() throws IOException {
        HttpResourceGroup group = Ribbon.createHttpResourceGroup("myclient", ClientOptions.create()
                .withConfigurationBasedServerList("localhost:12345").withMaxAutoRetriesNextServer(1));
        final String fallback = "fallback";
        HttpRequestTemplate<ByteBuf> template = group.newTemplateBuilder("test", ByteBuf.class).withUriTemplate("/")
                .withMethod("GET").withFallbackProvider(new FallbackHandler<ByteBuf>() {
                    @Override
                    public Observable<ByteBuf> getFallback(HystrixExecutableInfo<?> hystrixInfo,
                            Map<String, Object> requestProperties) {
                        try {
                            return Observable.just(Unpooled.buffer().writeBytes(fallback.getBytes("UTF-8")));
                        } catch (UnsupportedEncodingException e) {
                            return Observable.error(e);
                        }
                    }
                }).build();
        RibbonRequest<ByteBuf> request = template.requestBuilder().build();
        final AtomicReference<HystrixExecutableInfo<?>> hystrixInfo = new AtomicReference<HystrixExecutableInfo<?>>();
        final AtomicBoolean failed = new AtomicBoolean(false);
        Observable<String> result = request.withMetadata().toObservable()
                .flatMap(new Func1<RibbonResponse<Observable<ByteBuf>>, Observable<String>>() {
                    @Override
                    public Observable<String> call(final RibbonResponse<Observable<ByteBuf>> response) {
                        hystrixInfo.set(response.getHystrixInfo());
                        failed.set(response.getHystrixInfo().isFailedExecution());
                        return response.content().map(new Func1<ByteBuf, String>() {
                            @Override
                            public String call(ByteBuf t1) {
                                return t1.toString(Charset.defaultCharset());
                            }
                        });
                    }
                });
        String s = result.toBlocking().single();
        // this returns true only after the blocking call is done
        assertTrue(hystrixInfo.get().isResponseFromFallback());
        assertTrue(failed.get());
        assertEquals(fallback, s);
    }

    @Test
    public void testCacheHit() {
        HttpResourceGroup group = Ribbon.createHttpResourceGroup("myclient", ClientOptions.create()
                .withConfigurationBasedServerList("localhost:12345").withMaxAutoRetriesNextServer(1));
        final String content = "from cache";
        final String cacheKey = "somekey";
        HttpRequestTemplate<ByteBuf> template = group.newTemplateBuilder("test")
                .withCacheProvider(cacheKey, new CacheProvider<ByteBuf>() {
                    @Override
                    public Observable<ByteBuf> get(String key, Map<String, Object> vars) {
                        if (key.equals(cacheKey)) {
                            try {
                                return Observable.just(Unpooled.buffer().writeBytes(content.getBytes("UTF-8")));
                            } catch (UnsupportedEncodingException e) {
                                return Observable.error(e);
                            }
                        } else {
                            return Observable.error(new Exception("Cache miss"));
                        }
                    }
                }).withUriTemplate("/").withMethod("GET").build();
        RibbonRequest<ByteBuf> request = template.requestBuilder().build();
        String result = request.execute().toString(Charset.defaultCharset());
        assertEquals(content, result);
    }

    @Test
    public void testObserve() throws IOException, InterruptedException {
        MockWebServer server = new MockWebServer();
        String content = "Hello world";
        server.enqueue(
                new MockResponse().setResponseCode(200).setHeader("Content-type", "text/plain").setBody(content));
        server.enqueue(
                new MockResponse().setResponseCode(200).setHeader("Content-type", "text/plain").setBody(content));
        server.play();
        HttpResourceGroup group = Ribbon.createHttpResourceGroup("myclient",
                ClientOptions.create().withMaxAutoRetriesNextServer(3).withReadTimeout(300000)
                        .withConfigurationBasedServerList(
                                "localhost:12345, localhost:10092, localhost:" + server.getPort()));
        HttpRequestTemplate<ByteBuf> template = group.newTemplateBuilder("test", ByteBuf.class).withUriTemplate("/")
                .withMethod("GET").build();
        RibbonRequest<ByteBuf> request = template.requestBuilder().build();
        Observable<ByteBuf> result = request.observe();
        final CountDownLatch latch = new CountDownLatch(1);
        final AtomicReference<String> fromCommand = new AtomicReference<String>();
        // We need to wait until the response is received and processed by event loop
        // and make sure that subscribing to it again will not cause ByteBuf ref count issue
        result.toBlocking().last();
        result.subscribe(new Action1<ByteBuf>() {
            @Override
            public void call(ByteBuf t1) {
                try {
                    fromCommand.set(t1.toString(Charset.defaultCharset()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                latch.countDown();
            }
        });
        latch.await();
        assertEquals(content, fromCommand.get());

        Observable<RibbonResponse<Observable<ByteBuf>>> metaResult = request.withMetadata().observe();
        String result2 = "";
        // We need to wait until the response is received and processed by event loop
        // and make sure that subscribing to it again will not cause ByteBuf ref count issue
        metaResult.toBlocking().last();
        result2 = metaResult.flatMap(new Func1<RibbonResponse<Observable<ByteBuf>>, Observable<ByteBuf>>() {
            @Override
            public Observable<ByteBuf> call(RibbonResponse<Observable<ByteBuf>> t1) {
                return t1.content();
            }
        }).map(new Func1<ByteBuf, String>() {
            @Override
            public String call(ByteBuf t1) {
                return t1.toString(Charset.defaultCharset());
            }
        }).toBlocking().single();
        assertEquals(content, result2);
    }

    @Test
    public void testCacheMiss() throws IOException, InterruptedException {
        MockWebServer server = new MockWebServer();
        String content = "Hello world";
        server.enqueue(
                new MockResponse().setResponseCode(200).setHeader("Content-type", "text/plain").setBody(content));
        server.play();

        HttpResourceGroup group = Ribbon.createHttpResourceGroup("myclient", ClientOptions.create()
                .withConfigurationBasedServerList("localhost:" + server.getPort()).withMaxAutoRetriesNextServer(1));
        final String cacheKey = "somekey";
        HttpRequestTemplate<ByteBuf> template = group.newTemplateBuilder("test")
                .withCacheProvider(cacheKey, new CacheProvider<ByteBuf>() {
                    @Override
                    public Observable<ByteBuf> get(String key, Map<String, Object> vars) {
                        return Observable.error(new Exception("Cache miss again"));
                    }
                }).withMethod("GET").withUriTemplate("/").build();
        RibbonRequest<ByteBuf> request = template.requestBuilder().build();
        String result = toStringBlocking(request);
        assertEquals(content, result);
    }
}