This is an automated email from the ASF dual-hosted git repository.
reiern70 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/wicket.git
The following commit(s) were added to refs/heads/master by this push:
new 340a2d1870 [WICKET-7153] 1) make system mapper more reusable by making
newResourceReferenceMapper protected and 2) roll a hash code version of
ResourceReferenceMapper that encodes class names by using their hash code.
340a2d1870 is described below
commit 340a2d18700e6176a053c0fc1ea26626ca825312
Author: reiern70 <[email protected]>
AuthorDate: Sat Apr 12 13:35:30 2025 -0500
[WICKET-7153] 1) make system mapper more reusable by making
newResourceReferenceMapper protected and 2) roll a hash code version of
ResourceReferenceMapper that encodes class names by using their hash code.
---
.../HashBasedResourceReferenceMapperTest.java | 105 ++++++++++++
.../main/java/org/apache/wicket/SystemMapper.java | 6 +-
.../mapper/HashBasedResourceReferenceMapper.java | 182 +++++++++++++++++++++
3 files changed, 290 insertions(+), 3 deletions(-)
diff --git
a/wicket-core-tests/src/test/java/org/apache/wicket/core/request/mapper/HashBasedResourceReferenceMapperTest.java
b/wicket-core-tests/src/test/java/org/apache/wicket/core/request/mapper/HashBasedResourceReferenceMapperTest.java
new file mode 100644
index 0000000000..0df8105816
--- /dev/null
+++
b/wicket-core-tests/src/test/java/org/apache/wicket/core/request/mapper/HashBasedResourceReferenceMapperTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.core.request.mapper;
+
+import java.util.function.Supplier;
+import org.apache.wicket.Application;
+import org.apache.wicket.DefaultMapperContext;
+import org.apache.wicket.Page;
+import org.apache.wicket.SystemMapper;
+import org.apache.wicket.markup.head.CssHeaderItem;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.request.IRequestMapper;
+import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
+import org.apache.wicket.request.resource.CssResourceReference;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.request.resource.caching.IResourceCachingStrategy;
+import org.apache.wicket.resource.DummyApplication;
+import org.apache.wicket.util.tester.DummyHomePage;
+import org.apache.wicket.util.tester.WicketTester;
+import org.junit.jupiter.api.Test;
+
+class HashBasedResourceReferenceMapperTest {
+
+ public static class OtherPage extends DummyHomePage
+ {
+ private static final JavaScriptResourceReference
JAVA_SCRIPT_RESOURCE_REFERENCE = new
JavaScriptResourceReference(OtherPage.class, "a.js");
+ private static final CssResourceReference CSS_RESOURCE_REFERENCE = new
CssResourceReference(OtherPage.class, "b.css");
+
+ }
+
+ public static class HomePage extends DummyHomePage
+ {
+ private static final JavaScriptResourceReference
JAVA_SCRIPT_RESOURCE_REFERENCE = new
JavaScriptResourceReference(HomePage.class, "a.js");
+ private static final CssResourceReference CSS_RESOURCE_REFERENCE = new
CssResourceReference(HomePage.class, "b.css");
+
+ @Override
+ public void renderHead(IHeaderResponse response) {
+
response.render(JavaScriptHeaderItem.forReference(JAVA_SCRIPT_RESOURCE_REFERENCE));
+
response.render(JavaScriptHeaderItem.forReference(OtherPage.JAVA_SCRIPT_RESOURCE_REFERENCE));
+
response.render(CssHeaderItem.forReference(CSS_RESOURCE_REFERENCE));
+
response.render(CssHeaderItem.forReference(OtherPage.CSS_RESOURCE_REFERENCE));
+ }
+ }
+
+ @Test
+ void withJavaHash() {
+ final WebApplication dummyApplication = new DummyApplication() {
+ @Override
+ public Class<? extends Page> getHomePage() {
+ return HomePage.class;
+ }
+
+ @Override
+ protected IMapperContext newMapperContext() {
+ return new DefaultMapperContext(this) {
+ @Override
+ public String getNamespace() {
+ // we want to hide wicket from URL.
+ return "appres";
+ }
+ };
+ }
+
+
+ @Override
+ protected void init() {
+ super.init();
+ setRootRequestMapper(new SystemMapper(this) {
+ @Override
+ protected IRequestMapper
newResourceReferenceMapper(PageParametersEncoder pageParametersEncoder,
ParentFolderPlaceholderProvider parentFolderPlaceholderProvider,
Supplier<IResourceCachingStrategy> resourceCachingStrategy, Application
application) {
+ return
HashBasedResourceReferenceMapper.withJavaHash(pageParametersEncoder,
parentFolderPlaceholderProvider, resourceCachingStrategy, false);
+ }
+ });
+ }
+ };
+
+ final WicketTester tester = new WicketTester(dummyApplication);
+ long hash = HomePage.class.getName().hashCode();
+ long hash1 = OtherPage.class.getName().hashCode();
+ tester.startPage(HomePage.class);
+ tester.assertRenderedPage(HomePage.class);
+ // we check resource urls are hash encoded
+ tester.assertContains("./appres/resource/" + hash + "/a.js");
+ tester.assertContains("./appres/resource/" + hash1 + "/a.js");
+ tester.assertContains("./appres/resource/" + hash + "/b.css");
+ tester.assertContains("./appres/resource/" + hash1 + "/b.css");
+ tester.destroy();
+ }
+}
\ No newline at end of file
diff --git a/wicket-core/src/main/java/org/apache/wicket/SystemMapper.java
b/wicket-core/src/main/java/org/apache/wicket/SystemMapper.java
index 6f0084ca93..2140372003 100644
--- a/wicket-core/src/main/java/org/apache/wicket/SystemMapper.java
+++ b/wicket-core/src/main/java/org/apache/wicket/SystemMapper.java
@@ -51,7 +51,7 @@ public class SystemMapper extends CompoundRequestMapper
add(newBookmarkableMapper());
add(newHomePageMapper(new HomePageProvider(application)));
add(newResourceReferenceMapper(new PageParametersEncoder(),
- new ParentFolderPlaceholderProvider(application),
getResourceCachingStrategy()));
+ new ParentFolderPlaceholderProvider(application),
getResourceCachingStrategy(), application));
add(newUrlResourceReferenceMapper());
add(RestartResponseAtInterceptPageException.MAPPER);
add(newBufferedResponseMapper());
@@ -67,9 +67,9 @@ public class SystemMapper extends CompoundRequestMapper
return new UrlResourceReferenceMapper();
}
- private IRequestMapper newResourceReferenceMapper(PageParametersEncoder
pageParametersEncoder,
+ protected IRequestMapper
newResourceReferenceMapper(PageParametersEncoder pageParametersEncoder,
ParentFolderPlaceholderProvider parentFolderPlaceholderProvider,
-
Supplier<IResourceCachingStrategy> resourceCachingStrategy)
+
Supplier<IResourceCachingStrategy> resourceCachingStrategy, Application
application)
{
return new ResourceReferenceMapper(pageParametersEncoder,
parentFolderPlaceholderProvider,resourceCachingStrategy);
}
diff --git
a/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/HashBasedResourceReferenceMapper.java
b/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/HashBasedResourceReferenceMapper.java
new file mode 100644
index 0000000000..58c4816ab4
--- /dev/null
+++
b/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/HashBasedResourceReferenceMapper.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.core.request.mapper;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.request.mapper.ParentPathReferenceRewriter;
+import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder;
+import org.apache.wicket.request.resource.caching.IResourceCachingStrategy;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * <p>
+ * Resource reference {@link org.apache.wicket.request.IRequestMapper} that
encodes class names as hash codes.
+ * This allows hiding the class name from resource references. i.e., instead
of <em>/wicket/resource/org.xxx.yyy.ZPanel/a.js</em> the
+ * URL will display <em>/wicket/resource/ddd/a.js</em>, where ddd =
hash(org.xxx.yyy.ZPanel). This allows globally hiding
+ * class structure of your application (not displaying it via URLs).
+ * </p>
+ *
+ * <p>
+ * Caveat: we don't take into account hash collisions. I.e. two different
class names having the same hash code.
+ * </p>
+ * <p>
+ * Note: if you want to hide the "wicket" part of URL for "xxx" you can do:
+ * <code>
+ * protected IMapperContext newMapperContext() {
+ * return new DefaultMapperContext(this) {
+ * public String getNamespace() {
+ * return "xxx";
+ * }
+ * };
+ * }
+ * </code>
+ * on your Application class.
+ * </p>
+ * @author Ernesto Reinaldo Barreiro (reienr70 at gmail.com)
+ */
+public class HashBasedResourceReferenceMapper extends
ParentPathReferenceRewriter
+{
+ /**
+ * Knows how to compute the hash of a class name.
+ */
+ public interface IHasher {
+
+ long computeHash(String name);
+ }
+
+ public static class HashBasedBasicResourceReferenceMapper extends
BasicResourceReferenceMapper
+ {
+
+ private final Map<Long, String> hashMap = new
ConcurrentHashMap<>();
+
+ private final boolean checkHashCollision;
+
+ private final IHasher hasher;
+
+ /**
+ * Construct.
+ *
+ * @param pageParametersEncoder {@link IPageParametersEncoder}
+ * @param cachingStrategy Supplier for {@link
IResourceCachingStrategy}
+ * @param checkHashCollision whether it should check for hash
collisions or not.
+ * @param hasher method used to compute hash
+ */
+ public
HashBasedBasicResourceReferenceMapper(IPageParametersEncoder
pageParametersEncoder,
+
Supplier<? extends IResourceCachingStrategy>
cachingStrategy,
+
boolean checkHashCollision,
+
IHasher hasher)
+ {
+ super(pageParametersEncoder, cachingStrategy);
+ this.checkHashCollision = checkHashCollision;
+ Args.notNull(hasher, "hasher");
+ this.hasher = hasher;
+ }
+
+ @Override
+ protected Class<?> resolveClass(String name)
+ {
+ try
+ {
+ long hash = Long.parseLong(name);
+ String className = hashMap.get(hash);
+ if (className == null)
+ {
+ return super.getPageClass(name);
+ }
+ return super.resolveClass(className);
+ }
+ catch (NumberFormatException e)
+ {
+ return super.getPageClass(name);
+ }
+ }
+
+ @Override
+ protected String getClassName(Class<?> scope)
+ {
+ String name = super.getClassName(scope);
+ long hash = hasher.computeHash(name);
+ if (checkHashCollision) {
+ String existing = hashMap.get(hash);
+ if (existing != null && !existing.equals(name))
{
+ throw new WicketRuntimeException("Class
" + name + " has collision with " + existing);
+ }
+ }
+ hashMap.putIfAbsent(hash, name);
+ return Long.toString(hash);
+ }
+ }
+
+ /**
+ * Construct.
+ *
+ * @param pageParametersEncoder {@link IPageParametersEncoder}
+ * @param parentPathPartEscapeSequence Supplier fpr String
+ * @param cachingStrategy Supplier fo IResourceCachingStrategy
+ * @param checkHashCollision whether it should check for hash
collisions or not
+ * @param hasher method used to compute hash
+ */
+ public HashBasedResourceReferenceMapper(IPageParametersEncoder
pageParametersEncoder,
+
Supplier<String> parentPathPartEscapeSequence,
+
Supplier<IResourceCachingStrategy> cachingStrategy,
+
boolean checkHashCollision,
+
IHasher hasher)
+ {
+ super(new
HashBasedBasicResourceReferenceMapper(pageParametersEncoder, cachingStrategy,
checkHashCollision, hasher), parentPathPartEscapeSequence);
+ }
+
+ /**
+ * Create a HashBasedResourceReferenceMapper which uses standard java
hash implementation.
+ *
+ * @param pageParametersEncoder {@link IPageParametersEncoder}
+ * @param parentPathPartEscapeSequence Supplier for String
+ * @param cachingStrategy Supplier for {@link IResourceCachingStrategy}
+ * @param checkHashCollision whether it should check for hash
collisions or not
+ * @return HashBasedResourceReferenceMapper
+ */
+ public static HashBasedResourceReferenceMapper
withJavaHash(IPageParametersEncoder pageParametersEncoder,
+
Supplier<String>
parentPathPartEscapeSequence,
+
Supplier<IResourceCachingStrategy> cachingStrategy,
+
boolean checkHashCollision) {
+ return withOtherHash(pageParametersEncoder,
parentPathPartEscapeSequence, cachingStrategy, checkHashCollision, new
IHasher() {
+ @Override
+ public long computeHash(String name) {
+ return name != null ? name.hashCode() : 0;
+ }
+ });
+ }
+
+ /**
+ * Creates a HashBasedResourceReferenceMapper with a custom {@link
IHasher}
+ *
+ * @param pageParametersEncoder {@link IPageParametersEncoder}
+ * @param parentPathPartEscapeSequence {@link Supplier} for String
+ * @param cachingStrategy Supplier for {@link IResourceCachingStrategy}
+ * @param checkHashCollision whether it should check for hash
collisions or not
+ * @param hasher method used to compute hash
+ * @return HashBasedResourceReferenceMapper
+ */
+ public static HashBasedResourceReferenceMapper
withOtherHash(IPageParametersEncoder pageParametersEncoder,
+
Supplier<String>
parentPathPartEscapeSequence,
+
Supplier<IResourceCachingStrategy> cachingStrategy,
+
boolean checkHashCollision,
IHasher hasher) {
+ return new
HashBasedResourceReferenceMapper(pageParametersEncoder,
parentPathPartEscapeSequence, cachingStrategy, checkHashCollision, hasher);
+ }
+}