From ecda26c10199d134b39f10424f2dc1db31d5642b Mon Sep 17 00:00:00 2001 From: andreoss Date: Wed, 16 Sep 2020 16:36:00 -0400 Subject: [PATCH] (#999) Use `scalar.Sticky` for caching request --- src/main/java/org/takes/rq/RqOnce.java | 42 ++++++++-------- src/test/java/org/takes/rq/RqOnceTest.java | 57 +++++++++++++++------- 2 files changed, 60 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/takes/rq/RqOnce.java b/src/main/java/org/takes/rq/RqOnce.java index 9677e4809..3917f5321 100644 --- a/src/main/java/org/takes/rq/RqOnce.java +++ b/src/main/java/org/takes/rq/RqOnce.java @@ -23,8 +23,12 @@ */ package org.takes.rq; -import java.util.concurrent.atomic.AtomicBoolean; +import java.io.InputStream; import lombok.EqualsAndHashCode; +import org.cactoos.Scalar; +import org.cactoos.io.InputStreamOf; +import org.cactoos.scalar.Sticky; +import org.cactoos.text.TextOf; import org.takes.Request; /** @@ -33,9 +37,9 @@ *

The class is immutable and thread-safe. * * @since 0.36 - * @todo #918:30min Please use {@link org.cactoos.scalar.Sticky} decorator - * class so that multiple calls to body() would produce the same cached result - * rather than throwing an exception. + * @todo #999:30min Please use {@link org.cactoos.text.Sticky} decorator + * class instead of inner anonymous class below, which prevents input + * from being read twice by caching results of first call internally. */ @EqualsAndHashCode(callSuper = true) public final class RqOnce extends RqWrap { @@ -45,26 +49,20 @@ public final class RqOnce extends RqWrap { * @param req Original request */ public RqOnce(final Request req) { - super(RqOnce.wrap(req)); - } - - /** - * Wrap the request. - * @param req Request - * @return New request - */ - private static Request wrap(final Request req) { - final AtomicBoolean seen = new AtomicBoolean(false); - return new RequestOf( - req::head, - () -> { - if (!seen.getAndSet(true)) { - throw new IllegalStateException( - "It's not allowed to call body() more than once" + super( + new RequestOf( + new Sticky<>(req::head), + new Scalar() { + private final Scalar text = new Sticky<>( + new TextOf(req::body)::asString ); + + @Override + public InputStream value() throws Exception { + return new InputStreamOf(this.text.value()); + } } - return req.body(); - } + ) ); } } diff --git a/src/test/java/org/takes/rq/RqOnceTest.java b/src/test/java/org/takes/rq/RqOnceTest.java index e994f46ca..0187919df 100644 --- a/src/test/java/org/takes/rq/RqOnceTest.java +++ b/src/test/java/org/takes/rq/RqOnceTest.java @@ -25,37 +25,60 @@ import java.io.IOException; import org.cactoos.io.InputStreamOf; -import org.cactoos.text.Joined; -import org.junit.Test; +import org.cactoos.iterable.IterableOf; +import org.cactoos.text.Randomized; +import org.hamcrest.core.IsEqual; +import org.junit.jupiter.api.Test; +import org.llorllale.cactoos.matchers.Assertion; import org.takes.Request; /** * Test case for {@link RqOnce}. * @since 0.26 + * @checkstyle ClassDataAbstractionCouplingCheck (500 lines) */ public final class RqOnceTest { /** - * RqOnce can make request read-only-once. + * RqOnce can make request read-only-once for header. * @throws IOException If some problem inside */ - @Test(expected = IllegalStateException.class) - public void makesRequestReadOnlyOnce() throws IOException { + @Test + public void makesRequestReadOnlyOnceAndCachesHead() throws IOException { final Request req = new RqOnce( - new RqLive( - new InputStreamOf( - new Joined( - "\r\n", - "GET /test HTTP/1.1", - "Host: localhost", - "", - "... the body ..." - ) - ) + new RequestOf( + () -> new IterableOf<>(new Randomized().asString()), + () -> new InputStreamOf(new Randomized()) ) ); - new RqPrint(req).printBody(); - new RqPrint(req).printBody(); + new Assertion<>( + "the head must be cached", + new RqPrint(req).printHead(), + new IsEqual<>( + new RqPrint(req).printHead() + ) + ).affirm(); + } + + /** + * RqOnce can make request read-only-once for body. + * @throws IOException If some problem inside + */ + @Test + public void makesRequestReadOnlyOnceAndCachesBody() throws IOException { + final Request req = new RqOnce( + new RequestOf( + new IterableOf<>(new Randomized().asString()), + new InputStreamOf(new Randomized()) + ) + ); + new Assertion<>( + "the body must be cached", + new RqPrint(req).printBody(), + new IsEqual<>( + new RqPrint(req).printBody() + ) + ).affirm(); } }