You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by pa...@apache.org on 2020/01/15 20:27:21 UTC

[wicket] branch csp updated: WICKET-6729: Add a new API for managing IHeaderResponseDecorators

This is an automated email from the ASF dual-hosted git repository.

papegaaij pushed a commit to branch csp
in repository https://gitbox.apache.org/repos/asf/wicket.git


The following commit(s) were added to refs/heads/csp by this push:
     new 6043b09  WICKET-6729: Add a new API for managing IHeaderResponseDecorators
6043b09 is described below

commit 6043b09872518c55cd16a362752a6cdcaea8c42f
Author: Emond Papegaaij <em...@topicus.nl>
AuthorDate: Wed Jan 15 21:26:55 2020 +0100

    WICKET-6729: Add a new API for managing IHeaderResponseDecorators
---
 .../main/java/org/apache/wicket/Application.java   | 37 +++++++---
 .../html/HeaderResponseDecoratorCollection.java    | 82 ++++++++++++++++++++++
 .../head/filter/FilteringHeaderResponseTest.java   | 28 ++++----
 .../markup/html/DecoratingHeaderResponseTest.java  | 32 ++++-----
 .../apache/wicket/examples/csp/CspApplication.java |  2 +-
 .../ResourceDecorationApplication.java             | 12 ++--
 .../apache/wicket/examples/sri/SriApplication.java |  2 +-
 7 files changed, 142 insertions(+), 53 deletions(-)

diff --git a/wicket-core/src/main/java/org/apache/wicket/Application.java b/wicket-core/src/main/java/org/apache/wicket/Application.java
index d9b726a..af2632d 100644
--- a/wicket-core/src/main/java/org/apache/wicket/Application.java
+++ b/wicket-core/src/main/java/org/apache/wicket/Application.java
@@ -44,6 +44,7 @@ import org.apache.wicket.markup.MarkupFactory;
 import org.apache.wicket.markup.head.HeaderItem;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.ResourceAggregator;
+import org.apache.wicket.markup.html.HeaderResponseDecoratorCollection;
 import org.apache.wicket.markup.html.IHeaderContributor;
 import org.apache.wicket.markup.html.IHeaderResponseDecorator;
 import org.apache.wicket.markup.html.image.resource.DefaultButtonImageResourceFactory;
@@ -184,9 +185,8 @@ public abstract class Application implements UnboundListener, IEventSink, IMetad
 	/**
 	 * The decorator this application uses to decorate any header responses created by Wicket
 	 */
-	private IHeaderResponseDecorator headerResponseDecorator = (headerresponse) -> {
-		return new ResourceAggregator(headerresponse);
-	};
+	private HeaderResponseDecoratorCollection headerResponseDecorators =
+		new HeaderResponseDecoratorCollection();
 
 	/**
 	 * Checks if the <code>Application</code> threadlocal is set in this thread
@@ -1585,22 +1585,37 @@ public abstract class Application implements UnboundListener, IEventSink, IMetad
 	 * Sets an {@link IHeaderResponseDecorator} that you want your application to use to decorate
 	 * header responses.
 	 * <p>
-	 * Calling this method replaces the default decorator, which utilizes a {@link ResourceAggregator}:
-	 * The given implementation should make sure, that it too wraps responses in a {@link ResourceAggregator},
-	 * otherwise no dependencies for {@link HeaderItem}s will be resolved.   
+	 * Calling this method replaces the default decorator, which utilizes a
+	 * {@link ResourceAggregator}: The given implementation should make sure, that it too wraps
+	 * responses in a {@link ResourceAggregator}, otherwise no dependencies for {@link HeaderItem}s
+	 * will be resolved.
 	 * 
 	 * @param headerResponseDecorator
 	 *            your custom decorator, must not be null
+	 * @deprecated use {@code add(...)} on {@link #getHeaderResponseDecorators()}. This method
+	 *             removes the {@link ResourceAggregator}, which is needed to resolve resource
+	 *             dependencies.
 	 */
-	public final Application setHeaderResponseDecorator(final IHeaderResponseDecorator headerResponseDecorator)
+	@Deprecated
+	public final Application
+			setHeaderResponseDecorator(final IHeaderResponseDecorator headerResponseDecorator)
 	{
-		Args.notNull(headerResponseDecorator, "headerResponseDecorator");
-		
-		this.headerResponseDecorator = headerResponseDecorator;
+		headerResponseDecorators.replaceAll(headerResponseDecorator);
 		return this;
 	}
 
 	/**
+	 * Returns the {@link HeaderResponseDecoratorCollection} used by this application. On this
+	 * collection you can add additional decorators, which will be nested in the order added.
+	 * 
+	 * @return The {@link HeaderResponseDecoratorCollection} used by this application.
+	 */
+	public HeaderResponseDecoratorCollection getHeaderResponseDecorators()
+	{
+		return headerResponseDecorators;
+	}
+	
+	/**
 	 * INTERNAL METHOD - You shouldn't need to call this. This is called every time Wicket creates
 	 * an IHeaderResponse. It gives you the ability to incrementally add features to an
 	 * IHeaderResponse implementation by wrapping it in another implementation.
@@ -1615,7 +1630,7 @@ public abstract class Application implements UnboundListener, IEventSink, IMetad
 	 */
 	public final IHeaderResponse decorateHeaderResponse(final IHeaderResponse response)
 	{
-		return headerResponseDecorator.decorate(response);
+		return headerResponseDecorators.decorate(response);
 	}
 
 	/**
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/HeaderResponseDecoratorCollection.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/HeaderResponseDecoratorCollection.java
new file mode 100644
index 0000000..6d245ed
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/HeaderResponseDecoratorCollection.java
@@ -0,0 +1,82 @@
+package org.apache.wicket.markup.html;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.ResourceAggregator;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * A collection of {@link IHeaderResponseDecorator}s. The decorators will be nested oldest on the
+ * inside, newest on the outside. By default {@link ResourceAggregator} is already registered.
+ * 
+ * @author Emond Papegaaij
+ */
+public class HeaderResponseDecoratorCollection implements IHeaderResponseDecorator
+{
+	private final List<IHeaderResponseDecorator> decorators = new CopyOnWriteArrayList<>();
+
+	public HeaderResponseDecoratorCollection()
+	{
+		decorators.add(response -> new ResourceAggregator(response));
+	}
+
+	/**
+	 * Adds a new {@link IHeaderResponseDecorator} that will be invoked prior to all already
+	 * registered decorators. That means, the first to be added will be wrapped by a
+	 * {@link ResourceAggregator} like this: {@code new ResourceAggregator(first)}. The second will
+	 * be wrapped by the first and the aggregator: {@code new ResourceAggregator(first(second))}.
+	 * 
+	 * @param decorator
+	 *            The decorator to add, cannot be null.
+	 * @return {@code this} for chaining.
+	 */
+	public HeaderResponseDecoratorCollection add(IHeaderResponseDecorator decorator)
+	{
+		Args.notNull(decorator, "decorator");
+		decorators.add(0, decorator);
+		return this;
+	}
+
+	/**
+	 * Adds a new {@link IHeaderResponseDecorator} that will be invoked after all already registered
+	 * decorators.
+	 * 
+	 * @param decorator
+	 *            The decorator to add, cannot be null.
+	 * @return {@code this} for chaining.
+	 */
+	public HeaderResponseDecoratorCollection
+			addPostProcessingDecorator(IHeaderResponseDecorator decorator)
+	{
+		Args.notNull(decorator, "decorator");
+		decorators.add(decorator);
+		return this;
+	}
+
+	/**
+	 * Replaces all registered {@link IHeaderResponseDecorator}s with the given decorator. This also
+	 * removes the {@link ResourceAggregator}, which is required to render resource dependencies.
+	 * 
+	 * @param decorator
+	 *            The decorator to add, cannot be null.
+	 * @return {@code this} for chaining.
+	 */
+	public HeaderResponseDecoratorCollection replaceAll(IHeaderResponseDecorator decorator)
+	{
+		Args.notNull(decorator, "decorator");
+		decorators.clear();
+		decorators.add(decorator);
+		return this;
+	}
+
+	@Override
+	public IHeaderResponse decorate(IHeaderResponse response)
+	{
+		IHeaderResponse ret = response;
+		for (IHeaderResponseDecorator curDecorator : decorators)
+			ret = curDecorator.decorate(ret);
+		return ret;
+	}
+}
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/FilteringHeaderResponseTest.java b/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/FilteringHeaderResponseTest.java
index 70ea9d8..63bbadf 100644
--- a/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/FilteringHeaderResponseTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/FilteringHeaderResponseTest.java
@@ -41,17 +41,11 @@ class FilteringHeaderResponseTest extends WicketTestCase
 	@Test
 	void footerDependsOnHeadItem() throws Exception
 	{
-		tester.getApplication().setHeaderResponseDecorator(new IHeaderResponseDecorator()
-		{
-			@Override
-			public IHeaderResponse decorate(IHeaderResponse response)
-			{
-				// use this header resource decorator to load all JavaScript resources in the page
-				// footer (after </body>)
-				return new ResourceAggregator(
-					new JavaScriptFilteredIntoFooterHeaderResponse(response, "footerJS"));
-			}
-		});
+		// use this header resource decorator to load all JavaScript resources in the page
+		// footer (after </body>)
+		tester.getApplication()
+			.getHeaderResponseDecorators()
+			.add(response -> new JavaScriptFilteredIntoFooterHeaderResponse(response, "footerJS"));
 		executeTest(FilteredHeaderPage.class, "FilteredHeaderPageExpected.html");
 	}
 
@@ -90,19 +84,21 @@ class FilteringHeaderResponseTest extends WicketTestCase
 	@Test
 	void deferred() throws Exception
 	{
-		tester.getApplication().setHeaderResponseDecorator(
-			response -> new ResourceAggregator(new JavaScriptDeferHeaderResponse(response)));
+		tester.getApplication()
+			.getHeaderResponseDecorators()
+			.add(response -> new JavaScriptDeferHeaderResponse(response));
 		executeTest(DeferredPage.class, "DeferredPageExpected.html");
 	}
-	
+
 	/**
 	 * WICKET-6682
 	 */
 	@Test
 	void nonce() throws Exception
 	{
-		tester.getApplication().setHeaderResponseDecorator(
-			response -> new ResourceAggregator(new CspNonceHeaderResponse(response, "NONCE")));
+		tester.getApplication()
+			.getHeaderResponseDecorators()
+			.add(response -> new CspNonceHeaderResponse(response, "NONCE"));
 		executeTest(CspNoncePage.class, "CspNoncePageExpected.html");
 	}
 }
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/DecoratingHeaderResponseTest.java b/wicket-core/src/test/java/org/apache/wicket/markup/html/DecoratingHeaderResponseTest.java
index 65eb1ca..f166d41 100644
--- a/wicket-core/src/test/java/org/apache/wicket/markup/html/DecoratingHeaderResponseTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/DecoratingHeaderResponseTest.java
@@ -55,27 +55,25 @@ class DecoratingHeaderResponseTest extends WicketTestCase
 	void decoratedStringPrepend() throws IOException, ResourceStreamNotFoundException,
 		ParseException
 	{
-		tester.getApplication().setHeaderResponseDecorator(new IHeaderResponseDecorator()
-		{
-			@Override
-			public IHeaderResponse decorate(IHeaderResponse response)
+		tester.getApplication()
+			.getHeaderResponseDecorators()
+			.add(response -> new DecoratingHeaderResponse(response)
 			{
-				return new ResourceAggregator(new DecoratingHeaderResponse(response)
+				@Override
+				public void render(HeaderItem item)
 				{
-					@Override
-					public void render(HeaderItem item)
+					if (item instanceof JavaScriptReferenceHeaderItem)
 					{
-						if (item instanceof JavaScriptReferenceHeaderItem)
-						{
-							JavaScriptReferenceHeaderItem original = (JavaScriptReferenceHeaderItem)item;
-							item = JavaScriptHeaderItem.forReference(new PackageResourceReference(
-								"DECORATED-" + original.getReference().getName()), original.getId());
-						}
-						super.render(item);
+						JavaScriptReferenceHeaderItem original =
+							(JavaScriptReferenceHeaderItem) item;
+						item = JavaScriptHeaderItem.forReference(
+							new PackageResourceReference(
+								"DECORATED-" + original.getReference().getName()),
+							original.getId());
 					}
-				});
-			}
-		});
+					super.render(item);
+				}
+			});
 		tester.startPage(TestPage.class);
 		XmlPullParser parser = new XmlPullParser();
 		parser.parse(tester.getLastResponseAsString());
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/csp/CspApplication.java b/wicket-examples/src/main/java/org/apache/wicket/examples/csp/CspApplication.java
index b4b8341..e8eff7e 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/csp/CspApplication.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/csp/CspApplication.java
@@ -45,7 +45,7 @@ public class CspApplication extends WicketExampleApplication
 	{
 		super.init();
 
-		setHeaderResponseDecorator(response -> new ResourceAggregator(new CspNonceHeaderResponse(response, getNonce())));
+		getHeaderResponseDecorators().add(response -> new CspNonceHeaderResponse(response, getNonce()));
 		
 		mountPage("noncedemo", NonceDemoPage.class);
 	}
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/resourcedecoration/ResourceDecorationApplication.java b/wicket-examples/src/main/java/org/apache/wicket/examples/resourcedecoration/ResourceDecorationApplication.java
index b8b45e9..1fddce4 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/resourcedecoration/ResourceDecorationApplication.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/resourcedecoration/ResourceDecorationApplication.java
@@ -17,7 +17,6 @@
 package org.apache.wicket.examples.resourcedecoration;
 
 import org.apache.wicket.Application;
-import org.apache.wicket.markup.head.ResourceAggregator;
 import org.apache.wicket.markup.head.filter.JavaScriptFilteredIntoFooterHeaderResponse;
 import org.apache.wicket.markup.html.IHeaderResponseDecorator;
 import org.apache.wicket.protocol.http.WebApplication;
@@ -28,7 +27,7 @@ import org.apache.wicket.request.resource.CssResourceReference;
  * 
  * <p>
  * The key is to register a custom {@link IHeaderResponseDecorator} via
- * {@link Application#setHeaderResponseDecorator(IHeaderResponseDecorator)} that will intercept all
+ * {@link Application#getHeaderResponseDecorators()} that will intercept all
  * resource contributions.
  * 
  * @author jthomerson
@@ -44,11 +43,10 @@ public class ResourceDecorationApplication extends WebApplication
 			new CssResourceReference(HomePage.class, "footer.css"),
 			new CssResourceReference(HomePage.class, "header.css"));
 
-		setHeaderResponseDecorator(response -> {
-			// use this header resource decorator to load all JavaScript resources in the page
-			// footer (after </body>)
-			return new ResourceAggregator(new JavaScriptFilteredIntoFooterHeaderResponse(response, "footerJS"));
-		});
+		// use this header resource decorator to load all JavaScript resources in the page
+		// footer (after </body>)
+		getHeaderResponseDecorators()
+				.add(response -> new JavaScriptFilteredIntoFooterHeaderResponse(response, "footerJS"));
 	}
 
 	@Override
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/SriApplication.java b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/SriApplication.java
index 8b9a4f4..6221e60 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/sri/SriApplication.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/sri/SriApplication.java
@@ -35,7 +35,7 @@ public class SriApplication extends WicketExampleApplication
 	{
 		super.init();
 
-		setHeaderResponseDecorator(response -> new ResourceAggregator(integrity.wrap(response)));
+		getHeaderResponseDecorators().add(integrity::wrap);
 
 		mountPage("integritydemo", IntegrityDemoPage.class);
 	}