From 728ca50eb491c683f38b838616ff6b412819bb1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Willi=20Sch=C3=B6nborn?= Date: Thu, 25 Apr 2019 08:16:11 +0200 Subject: [PATCH] Added caching support for headers Fixes #425 --- .../main/java/org/zalando/logbook/Cache.java | 19 ++++++++++ .../zalando/logbook/CachingHttpRequest.java | 26 ++++++++++++++ .../zalando/logbook/CachingHttpResponse.java | 26 ++++++++++++++ .../org/zalando/logbook/DefaultLogbook.java | 9 +++-- .../logbook/CachingHttpRequestTest.java | 35 +++++++++++++++++++ .../logbook/CachingHttpResponseTest.java | 35 +++++++++++++++++++ 6 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 logbook-core/src/main/java/org/zalando/logbook/Cache.java create mode 100644 logbook-core/src/main/java/org/zalando/logbook/CachingHttpRequest.java create mode 100644 logbook-core/src/main/java/org/zalando/logbook/CachingHttpResponse.java create mode 100644 logbook-core/src/test/java/org/zalando/logbook/CachingHttpRequestTest.java create mode 100644 logbook-core/src/test/java/org/zalando/logbook/CachingHttpResponseTest.java diff --git a/logbook-core/src/main/java/org/zalando/logbook/Cache.java b/logbook-core/src/main/java/org/zalando/logbook/Cache.java new file mode 100644 index 000000000..565d26a3a --- /dev/null +++ b/logbook-core/src/main/java/org/zalando/logbook/Cache.java @@ -0,0 +1,19 @@ +package org.zalando.logbook; + +import lombok.AllArgsConstructor; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +@AllArgsConstructor +final class Cache { + + private final AtomicReference cache = new AtomicReference<>(); + private final Supplier supplier; + + public T get() { + return cache.updateAndGet(cached -> + cached == null ? supplier.get() : cached); + } + +} diff --git a/logbook-core/src/main/java/org/zalando/logbook/CachingHttpRequest.java b/logbook-core/src/main/java/org/zalando/logbook/CachingHttpRequest.java new file mode 100644 index 000000000..635330bf6 --- /dev/null +++ b/logbook-core/src/main/java/org/zalando/logbook/CachingHttpRequest.java @@ -0,0 +1,26 @@ +package org.zalando.logbook; + +import java.util.List; +import java.util.Map; + +final class CachingHttpRequest implements ForwardingHttpRequest { + + private final HttpRequest request; + private final Cache>> headers; + + CachingHttpRequest(final HttpRequest request) { + this.request = request; + this.headers = new Cache<>(request::getHeaders); + } + + @Override + public HttpRequest delegate() { + return request; + } + + @Override + public Map> getHeaders() { + return headers.get(); + } + +} diff --git a/logbook-core/src/main/java/org/zalando/logbook/CachingHttpResponse.java b/logbook-core/src/main/java/org/zalando/logbook/CachingHttpResponse.java new file mode 100644 index 000000000..c17b582ef --- /dev/null +++ b/logbook-core/src/main/java/org/zalando/logbook/CachingHttpResponse.java @@ -0,0 +1,26 @@ +package org.zalando.logbook; + +import java.util.List; +import java.util.Map; + +final class CachingHttpResponse implements ForwardingHttpResponse { + + private final HttpResponse response; + private final Cache>> headers; + + CachingHttpResponse(final HttpResponse response) { + this.response = response; + this.headers = new Cache<>(response::getHeaders); + } + + @Override + public HttpResponse delegate() { + return response; + } + + @Override + public Map> getHeaders() { + return headers.get(); + } + +} diff --git a/logbook-core/src/main/java/org/zalando/logbook/DefaultLogbook.java b/logbook-core/src/main/java/org/zalando/logbook/DefaultLogbook.java index 61ea14402..13981cc8f 100644 --- a/logbook-core/src/main/java/org/zalando/logbook/DefaultLogbook.java +++ b/logbook-core/src/main/java/org/zalando/logbook/DefaultLogbook.java @@ -26,15 +26,18 @@ public RequestWritingStage process(final HttpRequest originalRequest) throws IOE @Override public RequestWritingStage process(final HttpRequest originalRequest, final Strategy strategy) throws IOException { - if (sink.isActive() && predicate.test(originalRequest)) { + final HttpRequest request = new CachingHttpRequest(originalRequest); + + if (sink.isActive() && predicate.test(request)) { final Precorrelation precorrelation = new SimplePrecorrelation(clock); - final HttpRequest processedRequest = strategy.process(originalRequest); + final HttpRequest processedRequest = strategy.process(request); return () -> { final HttpRequest filteredRequest = requestFilter.filter(processedRequest); strategy.write(precorrelation, filteredRequest, sink); return originalResponse -> { - final HttpResponse processedResponse = strategy.process(originalRequest, originalResponse); + final HttpResponse response = new CachingHttpResponse(originalResponse); + final HttpResponse processedResponse = strategy.process(request, response); return () -> { final HttpResponse filteredResponse = responseFilter.filter(processedResponse); strategy.write(precorrelation.correlate(), filteredRequest, filteredResponse, sink); diff --git a/logbook-core/src/test/java/org/zalando/logbook/CachingHttpRequestTest.java b/logbook-core/src/test/java/org/zalando/logbook/CachingHttpRequestTest.java new file mode 100644 index 000000000..82aa1c572 --- /dev/null +++ b/logbook-core/src/test/java/org/zalando/logbook/CachingHttpRequestTest.java @@ -0,0 +1,35 @@ +package org.zalando.logbook; + +import org.junit.jupiter.api.Test; + +import static java.util.Collections.emptyMap; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.anEmptyMap; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +final class CachingHttpRequestTest { + + @Test + void shouldDelegate() { + final CachingHttpRequest unit = new CachingHttpRequest(MockHttpRequest.create()); + assertThat(unit.getHeaders(), is(anEmptyMap())); + } + + @Test + void shouldCache() { + final HttpRequest delegate = mock(HttpRequest.class); + when(delegate.getHeaders()).thenReturn(emptyMap()); + + final CachingHttpRequest unit = new CachingHttpRequest(delegate); + + unit.getHeaders(); + unit.getHeaders(); + + verify(delegate, atMost(1)).getHeaders(); + } + +} diff --git a/logbook-core/src/test/java/org/zalando/logbook/CachingHttpResponseTest.java b/logbook-core/src/test/java/org/zalando/logbook/CachingHttpResponseTest.java new file mode 100644 index 000000000..1fb32cb13 --- /dev/null +++ b/logbook-core/src/test/java/org/zalando/logbook/CachingHttpResponseTest.java @@ -0,0 +1,35 @@ +package org.zalando.logbook; + +import org.junit.jupiter.api.Test; + +import static java.util.Collections.emptyMap; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.anEmptyMap; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +final class CachingHttpResponseTest { + + @Test + void shouldDelegate() { + final CachingHttpResponse unit = new CachingHttpResponse(MockHttpResponse.create()); + assertThat(unit.getHeaders(), is(anEmptyMap())); + } + + @Test + void shouldCache() { + final HttpResponse delegate = mock(HttpResponse.class); + when(delegate.getHeaders()).thenReturn(emptyMap()); + + final CachingHttpResponse unit = new CachingHttpResponse(delegate); + + unit.getHeaders(); + unit.getHeaders(); + + verify(delegate, atMost(1)).getHeaders(); + } + +}