Skip to content

Commit

Permalink
(#999) Use scalar.Sticky for caching request
Browse files Browse the repository at this point in the history
  • Loading branch information
andreoss committed Sep 16, 2020
1 parent 6c01e74 commit ecda26c
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 39 deletions.
42 changes: 20 additions & 22 deletions src/main/java/org/takes/rq/RqOnce.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -33,9 +37,9 @@
* <p>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 {
Expand All @@ -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<InputStream>() {
private final Scalar<String> text = new Sticky<>(
new TextOf(req::body)::asString
);

@Override
public InputStream value() throws Exception {
return new InputStreamOf(this.text.value());
}
}
return req.body();
}
)
);
}
}
57 changes: 40 additions & 17 deletions src/test/java/org/takes/rq/RqOnceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

}

0 comments on commit ecda26c

Please sign in to comment.