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);
+       }
+}

Reply via email to