You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by be...@apache.org on 2022/08/28 12:37:10 UTC

[tapestry-5] branch master updated: TAP5-2736: CookieBuilder maxAge java.time.Duration support

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 <be...@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)
   }