diff --git a/jooby/src/main/java/io/jooby/Context.java b/jooby/src/main/java/io/jooby/Context.java index c1fe07a626..47a4d6b6ea 100644 --- a/jooby/src/main/java/io/jooby/Context.java +++ b/jooby/src/main/java/io/jooby/Context.java @@ -154,6 +154,13 @@ public interface Context extends Registry { */ @NonNull FlashMap flash(); + /** + * Flash map or null when no flash cookie exists. + * + * @return Flash map or null when no flash cookie exists. + */ + @Nullable FlashMap flashOrNull(); + /** * Get a flash attribute. * diff --git a/jooby/src/main/java/io/jooby/DefaultContext.java b/jooby/src/main/java/io/jooby/DefaultContext.java index 21302366ff..0ef010d4cf 100644 --- a/jooby/src/main/java/io/jooby/DefaultContext.java +++ b/jooby/src/main/java/io/jooby/DefaultContext.java @@ -110,6 +110,12 @@ default boolean matches(String pattern) { FlashMap.NAME, key -> FlashMap.create(this, getRouter().getFlashCookie().clone())); } + @Nullable @Override + default FlashMap flashOrNull() { + var flashCookie = cookie(getRouter().getFlashCookie().getName()); + return flashCookie.isMissing() ? null : flash(); + } + /** * Get a flash attribute. * diff --git a/jooby/src/main/java/io/jooby/ForwardingContext.java b/jooby/src/main/java/io/jooby/ForwardingContext.java index d83b4567b9..8b858050d4 100644 --- a/jooby/src/main/java/io/jooby/ForwardingContext.java +++ b/jooby/src/main/java/io/jooby/ForwardingContext.java @@ -673,6 +673,11 @@ public FlashMap flash() { return ctx.flash(); } + @Nullable @Override + public FlashMap flashOrNull() { + return ctx.flashOrNull(); + } + @NonNull @Override public Value flash(@NonNull String name) { return ctx.flash(name); diff --git a/jooby/src/main/java/io/jooby/TemplateEngine.java b/jooby/src/main/java/io/jooby/TemplateEngine.java index 9d24e81673..5c1d41b5f6 100644 --- a/jooby/src/main/java/io/jooby/TemplateEngine.java +++ b/jooby/src/main/java/io/jooby/TemplateEngine.java @@ -39,7 +39,7 @@ public interface TemplateEngine extends MessageEncoder { @Override default DataBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception { // initialize flash and session attributes (if any) - ctx.flash(); + ctx.flashOrNull(); ctx.sessionOrNull(); ctx.setDefaultResponseType(MediaType.html); diff --git a/jooby/src/test/java/io/jooby/Issue3607.java b/jooby/src/test/java/io/jooby/Issue3607.java new file mode 100644 index 0000000000..2cb31658af --- /dev/null +++ b/jooby/src/test/java/io/jooby/Issue3607.java @@ -0,0 +1,36 @@ +/* + * Jooby https://jooby.io + * Apache License Version 2.0 https://jooby.io/LICENSE.txt + * Copyright 2014 Edgar Espina + */ +package io.jooby; + +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Test; + +import io.jooby.buffer.DataBuffer; +import io.jooby.buffer.DefaultDataBufferFactory; + +public class Issue3607 { + + private static class TemplateEngineImpl implements TemplateEngine { + @Override + public DataBuffer render(Context ctx, ModelAndView modelAndView) throws Exception { + // do nothing + return DefaultDataBufferFactory.sharedInstance.wrap(new byte[0]); + } + } + + @Test + public void shouldNotGenerateEmptyFlashMap() throws Exception { + var ctx = mock(Context.class); + + var templateEngine = new TemplateEngineImpl(); + templateEngine.encode(ctx, ModelAndView.map("index.html")); + + verify(ctx, times(1)).flashOrNull(); + verify(ctx, times(1)).sessionOrNull(); + verify(ctx, times(1)).setDefaultResponseType(MediaType.html); + } +} diff --git a/tests/src/test/java/io/jooby/i3607/Issue3607.java b/tests/src/test/java/io/jooby/i3607/Issue3607.java new file mode 100644 index 0000000000..39fe818375 --- /dev/null +++ b/tests/src/test/java/io/jooby/i3607/Issue3607.java @@ -0,0 +1,52 @@ +/* + * Jooby https://jooby.io + * Apache License Version 2.0 https://jooby.io/LICENSE.txt + * Copyright 2014 Edgar Espina + */ +package io.jooby.i3607; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +import io.jooby.FlashMap; +import io.jooby.ModelAndView; +import io.jooby.junit.ServerTest; +import io.jooby.junit.ServerTestRunner; +import io.jooby.pebble.PebbleModule; + +public class Issue3607 { + @ServerTest + public void shouldNotGenerateEmptyFlashMap(ServerTestRunner runner) throws InterruptedException { + var latch = new CountDownLatch(1); + var mustBeNull = new AtomicBoolean(false); + runner + .define( + app -> { + app.install(new PebbleModule()); + app.use( + next -> + ctx -> { + ctx.onComplete( + done -> { + mustBeNull.set(!done.getAttributes().containsKey(FlashMap.NAME)); + latch.countDown(); + }); + return next.apply(ctx); + }); + app.get("/3607", ctx -> ModelAndView.map("index.pebble", Map.of("name", "Pebble"))); + }) + .ready( + client -> { + client.get( + "/3607", + rsp -> { + assertEquals("Hello Pebble!", rsp.body().string().trim()); + }); + }); + latch.await(); + assertTrue(mustBeNull.get(), "Flash map must be null"); + } +}