This is an automated email from the ASF dual-hosted git repository. benw pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tapestry-5.git
The following commit(s) were added to refs/heads/master by this push: new 71638e9cf TAP5-2736: CookieBuilder maxAge java.time.Duration support 71638e9cf is described below commit 71638e9cfef0ab598fc7c8e86f7db0c10d07150e Author: Ben Weidig <b...@netzgut.net> AuthorDate: Sun Aug 28 14:36:51 2022 +0200 TAP5-2736: CookieBuilder maxAge java.time.Duration support --- .../commons/internal/BasicTypeCoercions.java | 10 +++++ .../java/org/apache/tapestry5/CookieBuilder.java | 33 +++++++++++++++ .../tapestry5/internal/services/CookiesImpl.java | 3 +- .../internal/services/CookieBuilderSpec.groovy | 47 ++++++++++++++++++++++ .../test/groovy/ioc/specs/TypeCoercerSpec.groovy | 6 ++- 5 files changed, 97 insertions(+), 2 deletions(-) diff --git a/commons/src/main/java/org/apache/tapestry5/commons/internal/BasicTypeCoercions.java b/commons/src/main/java/org/apache/tapestry5/commons/internal/BasicTypeCoercions.java index ea5c7ed76..88251bfb6 100644 --- a/commons/src/main/java/org/apache/tapestry5/commons/internal/BasicTypeCoercions.java +++ b/commons/src/main/java/org/apache/tapestry5/commons/internal/BasicTypeCoercions.java @@ -33,6 +33,7 @@ import java.time.YearMonth; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -457,6 +458,15 @@ public class BasicTypeCoercions { add(configuration, Duration.class, Long.class, Duration::toNanos); add(configuration, Long.class, Duration.class, Duration::ofNanos); + add(configuration, Duration.class, TimeInterval.class, input -> { + // Duration.toMillis() is Java 9+, so we need to do it ourselves + long millisFromSeconds = input.getSeconds() * 1_000L; + long millisFromNanos = input.getNano() / 1_000_000L; + return new TimeInterval(millisFromSeconds + millisFromNanos); + }); + add(configuration, TimeInterval.class, Duration.class, input -> { + return Duration.ofMillis(input.milliseconds()); + }); } { diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/CookieBuilder.java b/tapestry-core/src/main/java/org/apache/tapestry5/CookieBuilder.java index 076f41b21..5bcbba681 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/CookieBuilder.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/CookieBuilder.java @@ -14,6 +14,8 @@ package org.apache.tapestry5; +import java.time.Duration; + import org.apache.tapestry5.http.services.Request; /** @@ -87,6 +89,37 @@ public abstract class CookieBuilder return this; } + + /** + * Set how long the cookie should live. A value of <code>java.time.Duration.ZERO</code> deletes a cookie, + * a negative value deletes a cookie upon closing the browser. The default is defined by + * the symbol <code>org.apache.tapestry5.default-cookie-max-age</code>. The factory default for + * this value is the equivalent of one week. + * + * @param maxAge + * the cookie's maximum age in seconds + * @return the modified {@link CookieBuilder} + * + * @since 5.8.3 + */ + public CookieBuilder setMaxAge(Duration duration) + { + long maxAgeAsLong = duration.getSeconds(); + if (maxAgeAsLong < 0L) + { + this.maxAge = -1; + } + else if (maxAgeAsLong >= Integer.MAX_VALUE) { + this.maxAge = Integer.MAX_VALUE; + } + else + { + this.maxAge = (int) maxAgeAsLong; + } + + return this; + } + /** * Set the cookie's secure mode. Defaults to {@link Request#isSecure()}. * diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookiesImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookiesImpl.java index 5d898a5cc..cccda7de5 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookiesImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookiesImpl.java @@ -17,6 +17,7 @@ package org.apache.tapestry5.internal.services; import javax.servlet.http.Cookie; import org.apache.tapestry5.CookieBuilder; +import org.apache.tapestry5.SymbolConstants; import org.apache.tapestry5.commons.util.TimeInterval; import org.apache.tapestry5.http.TapestryHttpSymbolConstants; import org.apache.tapestry5.http.services.Request; @@ -56,7 +57,7 @@ public class CookiesImpl implements Cookies @Symbol(TapestryHttpSymbolConstants.CONTEXT_PATH) String contextPath, - @Symbol("tapestry.default-cookie-max-age") @IntermediateType(TimeInterval.class) + @Symbol(SymbolConstants.COOKIE_MAX_AGE) @IntermediateType(TimeInterval.class) long defaultMaxAge) { this.request = request; diff --git a/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/CookieBuilderSpec.groovy b/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/CookieBuilderSpec.groovy new file mode 100644 index 000000000..568a623b2 --- /dev/null +++ b/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/CookieBuilderSpec.groovy @@ -0,0 +1,47 @@ +package org.apache.tapestry5.internal.services + +import java.time.Duration + +import org.apache.tapestry5.internal.test.TestableRequestImpl + +import spock.lang.Specification + +class CookieBuilderSpec extends Specification { + + static String CONTEXT_PATH = "/ctx" + + def createCookiesFixture(cookies) + { + + return new CookiesImpl( + new TestableRequestImpl(CONTEXT_PATH), + null, + [ addCookie: { cookies << it } ] as CookieSink, + CONTEXT_PATH, + 1_000L * 1_000L) + } + + def "write Cookie with maxAge as Duration"() + { + given: + def cookies = [] + def cookiesFixture = createCookiesFixture(cookies) + + when: + def builder = cookiesFixture.getBuilder("name", "value") + builder.maxAge = maxAge + builder.write() + + then: + cookies.size() == 1 + cookies[0].maxAge == expectedMaxAge + + where: + maxAge | expectedMaxAge + Duration.ZERO | 0 + Duration.ofMillis(-2) | -1 + Duration.ofHours(2L) | 60 * 60 * 2 + Duration.ofSeconds(Integer.MAX_VALUE + 1L) | Integer.MAX_VALUE + } + +} diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/TypeCoercerSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/TypeCoercerSpec.groovy index c9e358343..26f657727 100644 --- a/tapestry-ioc/src/test/groovy/ioc/specs/TypeCoercerSpec.groovy +++ b/tapestry-ioc/src/test/groovy/ioc/specs/TypeCoercerSpec.groovy @@ -180,7 +180,11 @@ class TypeCoercerSpec extends AbstractSharedRegistrySpecification { "P12Y1M7D" | Period | Period.of(12, 1, 7) ZonedDateTime.of(LocalDate.of(2020, 11, 29), LocalTime.of(13, 32, 12, 0), ZoneId.of("Europe/Berlin")).toEpochSecond() * 1_000 | Date | new Date(1606653132000L) - + + Duration.ofDays(7L) | TimeInterval | new TimeInterval("7 d"); + Duration.ofMillis(1234L) | TimeInterval | new TimeInterval("1234 ms"); + new TimeInterval("1d 3h 12ms") | Duration | Duration.ofDays(1L).plusHours(3L).plusMillis(12) + inputTypeName = PlasticUtils.toTypeName(input.getClass()) typeName = PlasticUtils.toTypeName(type) }