You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by mg...@apache.org on 2012/03/07 09:39:03 UTC

[14/19] WICKET-4439 Move classes around so that there are no two packages with the same name in different modules

http://git-wip-us.apache.org/repos/asf/wicket/blob/53f07873/wicket-core/src/test/java/org/apache/wicket/ExceptionMapperTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/ExceptionMapperTest.java b/wicket-core/src/test/java/org/apache/wicket/ExceptionMapperTest.java
index 921513d..aabe9f0 100644
--- a/wicket-core/src/test/java/org/apache/wicket/ExceptionMapperTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/ExceptionMapperTest.java
@@ -21,8 +21,8 @@ import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.markup.html.link.Link;
 import org.apache.wicket.request.IExceptionMapper;
 import org.apache.wicket.request.IRequestHandler;
-import org.apache.wicket.request.handler.PageProvider;
-import org.apache.wicket.request.handler.RenderPageRequestHandler;
+import org.apache.wicket.core.request.handler.PageProvider;
+import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
 import org.apache.wicket.util.IProvider;
 import org.apache.wicket.util.lang.Exceptions;
 import org.apache.wicket.util.resource.IResourceStream;

http://git-wip-us.apache.org/repos/asf/wicket/blob/53f07873/wicket-core/src/test/java/org/apache/wicket/core/request/handler/ListenerInterfaceRequestHandlerTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/core/request/handler/ListenerInterfaceRequestHandlerTest.java b/wicket-core/src/test/java/org/apache/wicket/core/request/handler/ListenerInterfaceRequestHandlerTest.java
new file mode 100644
index 0000000..3321840
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/core/request/handler/ListenerInterfaceRequestHandlerTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.handler;
+
+import java.io.IOException;
+import java.text.ParseException;
+
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.Page;
+import org.apache.wicket.RequestListenerInterface;
+import org.apache.wicket.Session;
+import org.apache.wicket.WicketTestCase;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.markup.IMarkupResourceStreamProvider;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.link.ILinkListener;
+import org.apache.wicket.request.Url;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
+import org.apache.wicket.util.resource.StringResourceStream;
+import org.junit.Test;
+
+/**
+ * Tests for {@link ListenerInterfaceRequestHandler}
+ */
+public class ListenerInterfaceRequestHandlerTest extends WicketTestCase
+{
+
+	/**
+	 * https://issues.apache.org/jira/browse/WICKET-4116
+	 * 
+	 * @throws Exception
+	 */
+	@Test
+	public void recreateThePageWhenListenereInterfaceIsExecutedOnExpiredPage() throws Exception
+	{
+		tester.getApplication().mountPage("ajaxLink", AjaxLinkExpirePage.class);
+		AjaxLinkExpirePage page = tester.startPage(AjaxLinkExpirePage.class);
+
+		int initialPageId = page.getPageId();
+
+		Url urlToAjaxLink = tester.urlFor(page.link);
+		Session session = tester.getSession();
+		session.getPageManager().sessionExpired(session.getId());
+
+		// fire a request to the ajax link on the expired page
+		executeAjaxUrlWithLastBaseUrl(urlToAjaxLink);
+
+		Page lastRenderedPage = tester.getLastRenderedPage();
+		int lastRenderedPageId = lastRenderedPage.getPageId();
+		assertTrue("A new page must be create ", lastRenderedPageId > initialPageId);
+	}
+
+	private void executeAjaxUrlWithLastBaseUrl(Url url) throws IOException,
+		ResourceStreamNotFoundException, ParseException
+	{
+		tester.getRequest().setUrl(url);
+		tester.getRequest().addHeader("Wicket-Ajax-BaseURL",
+			tester.getWicketAjaxBaseUrlEncodedInLastResponse());
+		tester.getRequest().addHeader("Wicket-Ajax", "true");
+		tester.processRequest();
+	}
+
+	/**
+	 * Test page for #recreateThePageWhenListenereInterfaceIsExecutedOnExpiredPage()
+	 */
+	public static class AjaxLinkExpirePage extends WebPage implements IMarkupResourceStreamProvider
+	{
+		private static final long serialVersionUID = 1L;
+
+		private AjaxLink<Void> link;
+
+		/**
+		 * Constructor.
+		 */
+		public AjaxLinkExpirePage()
+		{
+			add(link = new AjaxLink<Void>("test")
+			{
+				private static final long serialVersionUID = 1L;
+
+				@Override
+				public void onClick(AjaxRequestTarget target)
+				{
+					System.err.println("clicked");
+				}
+			});
+		}
+
+		@Override
+		public IResourceStream getMarkupResourceStream(MarkupContainer container,
+			Class<?> containerClass)
+		{
+
+			return new StringResourceStream(
+				"<html><body><a wicket:id='test'>Link</a></body></html>");
+		}
+
+	}
+
+	/**
+	 * Testcase for WICKET-4185
+	 */
+	@Test
+	public void isPageInstanceCreatedOnClassLinks()
+	{
+		PageAndComponentProvider provider = new PageAndComponentProvider(Page.class, "link");
+		ListenerInterfaceRequestHandler handler = new ListenerInterfaceRequestHandler(provider,
+			RequestListenerInterface.forName(ILinkListener.class.getSimpleName()));
+		assertFalse("Handler should not report a page instance is available ",
+			handler.isPageInstanceCreated());
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/53f07873/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/AbstractMapperTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/AbstractMapperTest.java b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/AbstractMapperTest.java
new file mode 100644
index 0000000..8d0a4a2
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/AbstractMapperTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.nio.charset.Charset;
+import java.util.Locale;
+
+import org.apache.wicket.RequestListenerInterface;
+import org.apache.wicket.markup.html.link.ILinkListener;
+import org.apache.wicket.request.Request;
+import org.apache.wicket.request.Url;
+import org.apache.wicket.request.component.IRequestablePage;
+import org.junit.Assert;
+import org.junit.Before;
+
+/**
+ * @author Matej Knopp
+ */
+public abstract class AbstractMapperTest extends Assert
+{
+	/**
+	 * Construct.
+	 */
+	public AbstractMapperTest()
+	{
+	}
+
+	protected TestMapperContext context = new TestMapperContext();
+
+	/**
+	 * @throws Exception
+	 */
+	@Before
+	public void before() throws Exception
+	{
+		// inititalize the interface
+		RequestListenerInterface i = ILinkListener.INTERFACE;
+	}
+
+	protected Request getRequest(final Url url)
+	{
+		return new Request()
+		{
+			@Override
+			public Url getUrl()
+			{
+				return url;
+			}
+
+			@Override
+			public Locale getLocale()
+			{
+				return null;
+			}
+
+			@Override
+			public Charset getCharset()
+			{
+				return Charset.forName("UTF-8");
+			}
+
+			@Override
+			public Url getClientUrl()
+			{
+				return url;
+			}
+
+			@Override
+			public Object getContainerRequest()
+			{
+				return null;
+			}
+		};
+	}
+
+	protected void checkPage(IRequestablePage page, int id)
+	{
+		assertEquals(id, page.getPageId());
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/53f07873/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/AbstractResourceReferenceMapperTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/AbstractResourceReferenceMapperTest.java b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/AbstractResourceReferenceMapperTest.java
new file mode 100644
index 0000000..123fbb1
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/AbstractResourceReferenceMapperTest.java
@@ -0,0 +1,224 @@
+/*
+ * 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.Locale;
+
+import org.apache.wicket.request.resource.IResource;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.junit.Before;
+
+/**
+ * @author Matej Knopp
+ */
+public abstract class AbstractResourceReferenceMapperTest extends AbstractMapperTest
+{
+	/**
+	 * Construct.
+	 */
+	public AbstractResourceReferenceMapperTest()
+	{
+	}
+
+	protected final IResource resource1 = new IResource()
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public void respond(Attributes attributes)
+		{
+		}
+	};
+
+	protected final IResource resource2 = new IResource()
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public void respond(Attributes attributes)
+		{
+		}
+	};
+
+	protected final IResource resource3 = new IResource()
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public void respond(Attributes attributes)
+		{
+		}
+	};
+
+	protected final IResource resource4 = new IResource()
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public void respond(Attributes attributes)
+		{
+		}
+	};
+
+	protected final IResource resource5 = new IResource()
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public void respond(Attributes attributes)
+		{
+		}
+	};
+
+	protected final IResource resource6 = new IResource()
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public void respond(Attributes attributes)
+		{
+		}
+	};
+
+	protected String CLASS_NAME = AbstractResourceReferenceMapperTest.class.getName();
+
+	protected ResourceReference reference1 = new ResourceReference(
+		AbstractResourceReferenceMapperTest.class, "reference1", null, null, null)
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public IResource getResource()
+		{
+			return resource1;
+		}
+	};
+
+	protected ResourceReference reference1_a = new ResourceReference(
+		AbstractResourceReferenceMapperTest.class, "reference1", Locale.ENGLISH, null, null)
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public IResource getResource()
+		{
+			return resource1;
+		}
+	};
+
+	protected ResourceReference reference1_b = new ResourceReference(
+		AbstractResourceReferenceMapperTest.class, "reference1", null, "style", null)
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public IResource getResource()
+		{
+			return resource1;
+		}
+	};
+
+	protected ResourceReference reference2 = new ResourceReference(
+		AbstractResourceReferenceMapperTest.class, "reference2/name2", new Locale("en", "en"),
+		null, null)
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public IResource getResource()
+		{
+			return resource2;
+		}
+	};
+
+	protected ResourceReference reference2_a = new ResourceReference(
+		AbstractResourceReferenceMapperTest.class, "reference2/name2", new Locale("en", "en"),
+		"style", null)
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public IResource getResource()
+		{
+			return resource2;
+		}
+	};
+
+	protected ResourceReference reference3 = new ResourceReference(
+		AbstractResourceReferenceMapperTest.class, "reference3", null, "style", null)
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public IResource getResource()
+		{
+			return resource3;
+		}
+	};
+
+	protected ResourceReference reference4 = new ResourceReference(
+		AbstractResourceReferenceMapperTest.class, "reference4", Locale.ENGLISH, "style", null)
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public IResource getResource()
+		{
+			return resource4;
+		}
+	};
+
+	protected ResourceReference reference5 = new ResourceReference(
+		AbstractResourceReferenceMapperTest.class, "reference5", Locale.ENGLISH, null, "variation")
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public IResource getResource()
+		{
+			return resource5;
+		}
+	};
+
+	protected ResourceReference reference6 = new ResourceReference(
+		AbstractResourceReferenceMapperTest.class, "reference6", Locale.ENGLISH, "style",
+		"variation")
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public IResource getResource()
+		{
+			return resource6;
+		}
+	};
+
+	@Override
+	@Before
+	public void before() throws Exception
+	{
+		context.getResourceReferenceRegistry().registerResourceReference(reference1);
+		context.getResourceReferenceRegistry().registerResourceReference(reference1_a);
+		context.getResourceReferenceRegistry().registerResourceReference(reference1_b);
+		context.getResourceReferenceRegistry().registerResourceReference(reference2);
+		context.getResourceReferenceRegistry().registerResourceReference(reference2_a);
+		context.getResourceReferenceRegistry().registerResourceReference(reference3);
+		context.getResourceReferenceRegistry().registerResourceReference(reference4);
+		context.getResourceReferenceRegistry().registerResourceReference(reference5);
+		context.getResourceReferenceRegistry().registerResourceReference(reference6);
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/53f07873/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapperTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapperTest.java b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapperTest.java
new file mode 100644
index 0000000..d66cf57
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapperTest.java
@@ -0,0 +1,563 @@
+/*
+ * 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.io.Serializable;
+import java.util.Locale;
+
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.Url;
+import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
+import org.apache.wicket.request.mapper.parameter.INamedParameters;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
+import org.apache.wicket.request.resource.IResource;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.request.resource.caching.FilenameWithVersionResourceCachingStrategy;
+import org.apache.wicket.request.resource.caching.IResourceCachingStrategy;
+import org.apache.wicket.request.resource.caching.IStaticCacheableResource;
+import org.apache.wicket.request.resource.caching.NoOpResourceCachingStrategy;
+import org.apache.wicket.request.resource.caching.ResourceUrl;
+import org.apache.wicket.request.resource.caching.version.StaticResourceVersion;
+import org.apache.wicket.util.IProvider;
+import org.apache.wicket.util.ValueProvider;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.resource.StringResourceStream;
+import org.junit.Test;
+
+/**
+ * @author Matej Knopp
+ */
+public class BasicResourceReferenceMapperTest extends AbstractResourceReferenceMapperTest
+{
+	private static final IProvider<IResourceCachingStrategy> NO_CACHING = new ValueProvider<IResourceCachingStrategy>(
+		NoOpResourceCachingStrategy.INSTANCE);
+
+	private final BasicResourceReferenceMapper encoder = new BasicResourceReferenceMapper(
+		new PageParametersEncoder(), NO_CACHING)
+	{
+		@Override
+		protected IMapperContext getContext()
+		{
+			return context;
+		}
+	};
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode1()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference1");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource1, h.getResource());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals(0, h.getPageParameters().getNamedKeys().size());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode1A()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference1?en");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource1, h.getResource());
+		assertEquals(Locale.ENGLISH, h.getLocale());
+		assertEquals(null, h.getStyle());
+		assertEquals(null, h.getVariation());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals(0, h.getPageParameters().getNamedKeys().size());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode2()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference1?p1=v1&p2=v2");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource1, h.getResource());
+		assertEquals(null, h.getLocale());
+		assertEquals(null, h.getStyle());
+		assertEquals(null, h.getVariation());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals("v1", h.getPageParameters().get("p1").toString());
+		assertEquals("v2", h.getPageParameters().get("p2").toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode2A()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference1?-style&p1=v1&p2=v2");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource1, h.getResource());
+		assertEquals(null, h.getLocale());
+		assertEquals("style", h.getStyle());
+		assertEquals(null, h.getVariation());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals("v1", h.getPageParameters().get("p1").toString());
+		assertEquals("v2", h.getPageParameters().get("p2").toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode3()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference2/name2?en_EN");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource2, h.getResource());
+		assertEquals(new Locale("en", "en"), h.getLocale());
+		assertEquals(null, h.getStyle());
+		assertEquals(null, h.getVariation());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals(0, h.getPageParameters().getNamedKeys().size());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode3A()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference2/name2?en_EN-style");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource2, h.getResource());
+		assertEquals(new Locale("en", "en"), h.getLocale());
+		assertEquals("style", h.getStyle());
+		assertEquals(null, h.getVariation());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals(0, h.getPageParameters().getNamedKeys().size());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode3B()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference2/name2");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertNull(handler);
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode4()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference2/name2?en_EN&p1=v1&p2=v2");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource2, h.getResource());
+		assertEquals(new Locale("en", "en"), h.getLocale());
+		assertEquals(null, h.getStyle());
+		assertEquals(null, h.getVariation());
+		assertEquals("v1", h.getPageParameters().get("p1").toString());
+		assertEquals("v2", h.getPageParameters().get("p2").toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode5()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference3?-style");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource3, h.getResource());
+		assertEquals(null, h.getLocale());
+		assertEquals("style", h.getStyle());
+		assertEquals(null, h.getVariation());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals(0, h.getPageParameters().getNamedKeys().size());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode6()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference3?-style&p1=v1&p2=v2");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource3, h.getResource());
+		assertEquals(null, h.getLocale());
+		assertEquals("style", h.getStyle());
+		assertEquals(null, h.getVariation());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals("v1", h.getPageParameters().get("p1").toString());
+		assertEquals("v2", h.getPageParameters().get("p2").toString());
+	}
+
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode7()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference4?en-style");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource4, h.getResource());
+		assertEquals(Locale.ENGLISH, h.getLocale());
+		assertEquals("style", h.getStyle());
+		assertEquals(null, h.getVariation());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals(0, h.getPageParameters().getNamedKeys().size());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode7A()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference4?sk");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertNull(handler);
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode8()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/reference4?en-style&p1=v1&p2=v2");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource4, h.getResource());
+		assertEquals(Locale.ENGLISH, h.getLocale());
+		assertEquals("style", h.getStyle());
+		assertEquals(null, h.getVariation());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals("v1", h.getPageParameters().get("p1").toString());
+		assertEquals("v2", h.getPageParameters().get("p2").toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode9()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME +
+			"/reference5?en--variation&p1=v1&p2=v2");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource5, h.getResource());
+		assertEquals(Locale.ENGLISH, h.getLocale());
+		assertEquals(null, h.getStyle());
+		assertEquals("variation", h.getVariation());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals("v1", h.getPageParameters().get("p1").toString());
+		assertEquals("v2", h.getPageParameters().get("p2").toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode10()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME +
+			"/reference6?en-style-variation&p1=v1&p2=v2");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler h = (ResourceReferenceRequestHandler)handler;
+		assertEquals(resource6, h.getResource());
+		assertEquals(Locale.ENGLISH, h.getLocale());
+		assertEquals("style", h.getStyle());
+		assertEquals("variation", h.getVariation());
+		assertEquals(0, h.getPageParameters().getIndexedCount());
+		assertEquals("v1", h.getPageParameters().get("p1").toString());
+		assertEquals("v2", h.getPageParameters().get("p2").toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode1()
+	{
+		ResourceReferenceRequestHandler handler = new ResourceReferenceRequestHandler(reference1,
+			null);
+		Url url = encoder.mapHandler(handler);
+		assertEquals("wicket/resource/" + CLASS_NAME + "/reference1", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode2()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "X");
+		parameters.add("p1", "v1");
+		parameters.add("p2", "v2");
+		ResourceReferenceRequestHandler handler = new ResourceReferenceRequestHandler(reference1,
+			parameters);
+
+		Url url = encoder.mapHandler(handler);
+		assertEquals("wicket/resource/" + CLASS_NAME + "/reference1?p1=v1&p2=v2", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode3()
+	{
+		ResourceReferenceRequestHandler handler = new ResourceReferenceRequestHandler(reference2,
+			null);
+		Url url = encoder.mapHandler(handler);
+		assertEquals("wicket/resource/" + CLASS_NAME + "/reference2/name2?en_EN", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode4()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "X");
+		parameters.add("p1", "v1");
+		parameters.add("p2", "v2");
+		ResourceReferenceRequestHandler handler = new ResourceReferenceRequestHandler(reference2,
+			parameters);
+
+		Url url = encoder.mapHandler(handler);
+		assertEquals("wicket/resource/" + CLASS_NAME + "/reference2/name2?en_EN&p1=v1&p2=v2",
+				url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode5()
+	{
+		ResourceReferenceRequestHandler handler = new ResourceReferenceRequestHandler(reference3,
+			null);
+		Url url = encoder.mapHandler(handler);
+		assertEquals("wicket/resource/" + CLASS_NAME + "/reference3?-style", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode6()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "X");
+		parameters.add("p1", "v1");
+		parameters.add("p2", "v2");
+		ResourceReferenceRequestHandler handler = new ResourceReferenceRequestHandler(reference3,
+			parameters);
+
+		Url url = encoder.mapHandler(handler);
+		assertEquals("wicket/resource/" + CLASS_NAME + "/reference3?-style&p1=v1&p2=v2",
+				url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode7()
+	{
+		ResourceReferenceRequestHandler handler = new ResourceReferenceRequestHandler(reference4,
+			null);
+		Url url = encoder.mapHandler(handler);
+		assertEquals("wicket/resource/" + CLASS_NAME + "/reference4?en-style", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode8()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "X");
+		parameters.add("p1", "v1");
+		parameters.add("p2", "v2");
+		ResourceReferenceRequestHandler handler = new ResourceReferenceRequestHandler(reference4,
+			parameters);
+
+		Url url = encoder.mapHandler(handler);
+		assertEquals("wicket/resource/" + CLASS_NAME + "/reference4?en-style&p1=v1&p2=v2",
+				url.toString());
+	}
+
+	/**
+	 * Tests request to url encoding when style is null but variation is not
+	 */
+	@Test
+	public void encode9()
+	{
+		ResourceReferenceRequestHandler handler = new ResourceReferenceRequestHandler(reference5,
+			null);
+
+		Url url = encoder.mapHandler(handler);
+		assertEquals("wicket/resource/" + CLASS_NAME + "/reference5?en--variation", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void versionStringInResourceFilename()
+	{
+		final IStaticCacheableResource resource = new IStaticCacheableResource()
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public Serializable getCacheKey()
+			{
+				return null;
+			}
+
+			@Override
+			public IResourceStream getCacheableResourceStream()
+			{
+				return new StringResourceStream("foo-bar");
+			}
+
+			@Override
+			public void respond(Attributes attributes)
+			{
+			}
+		};
+
+		final ResourceReference reference = new ResourceReference(getClass(), "versioned",
+			Locale.ENGLISH, "style", null)
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public IResource getResource()
+			{
+				return resource;
+			}
+		};
+
+		IResourceCachingStrategy strategy = new FilenameWithVersionResourceCachingStrategy(
+			"-version-", new StaticResourceVersion("foobar"));
+
+		INamedParameters params = new PageParameters();
+		ResourceUrl url = new ResourceUrl("test.js", params);
+		strategy.decorateUrl(url, resource);
+		assertEquals("test-version-foobar.js", url.getFileName());
+		strategy.undecorateUrl(url);
+		assertEquals("test.js", url.getFileName());
+
+		url = new ResourceUrl("test", params);
+		strategy.decorateUrl(url, resource);
+		assertEquals("test-version-foobar", url.getFileName());
+		strategy.undecorateUrl(url);
+		assertEquals("test", url.getFileName());
+
+		// this behavior is o.k. since a browser could request an
+		// previous version of the resource. for example we
+		// could first have 'test-alpha.txt' which would be later replaced
+		// by 'test-beta.txt' but in any case will point to
+		// internal resource 'test.txt'
+		url = new ResourceUrl("test-version-older.txt", params);
+		strategy.undecorateUrl(url);
+		assertEquals("test.txt", url.getFileName());
+
+		// weird but valid
+		url = new ResourceUrl("test-version-.txt", params);
+		strategy.undecorateUrl(url);
+		assertEquals("test.txt", url.getFileName());
+
+		// weird but valid
+		url = new ResourceUrl("test-version--------", params);
+		strategy.undecorateUrl(url);
+		assertEquals("test", url.getFileName());
+
+		// weird but valid
+		url = new ResourceUrl("test-version-1.0.3-alpha.txt", params);
+		strategy.undecorateUrl(url);
+		assertEquals("test.txt", url.getFileName());
+
+		// check a version that contains a dot which also marks the filename extension
+		strategy = new FilenameWithVersionResourceCachingStrategy("-version-",
+			new StaticResourceVersion("1.0.4-beta"));
+		url = new ResourceUrl("test.txt", params);
+		strategy.decorateUrl(url, resource);
+		assertEquals("test-version-1.0.4-beta.txt", url.getFileName());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void requestWithEmptyFilename()
+	{
+		Url url = Url.parse("wicket/resource/" + CLASS_NAME + "/");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertNull(handler);
+	}
+
+	/**
+	 * Tests <a href="https://issues.apache.org/jira/browse/WICKET-3918">WICKET-3918</a>.
+	 */
+	@Test
+	public void wicket3918()
+	{
+		Url url = Url.parse("wicket/resource/org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow/res/");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertNull(handler);
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/53f07873/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BookmarkableMapperTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BookmarkableMapperTest.java b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BookmarkableMapperTest.java
new file mode 100644
index 0000000..f4d0a3f
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BookmarkableMapperTest.java
@@ -0,0 +1,409 @@
+/*
+ * 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 org.apache.wicket.MockPage;
+import org.apache.wicket.core.request.handler.BookmarkableListenerInterfaceRequestHandler;
+import org.apache.wicket.markup.html.link.ILinkListener;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.Url;
+import org.apache.wicket.request.component.IRequestableComponent;
+import org.apache.wicket.request.component.IRequestablePage;
+import org.apache.wicket.core.request.handler.BookmarkablePageRequestHandler;
+import org.apache.wicket.core.request.handler.IPageProvider;
+import org.apache.wicket.core.request.handler.IPageRequestHandler;
+import org.apache.wicket.core.request.handler.ListenerInterfaceRequestHandler;
+import org.apache.wicket.core.request.handler.PageAndComponentProvider;
+import org.apache.wicket.core.request.handler.PageProvider;
+import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.junit.Test;
+
+/**
+ * @author Matej Knopp
+ */
+public class BookmarkableMapperTest extends AbstractMapperTest
+{
+
+	private final BookmarkableMapper encoder = new BookmarkableMapper()
+	{
+		@Override
+		protected IMapperContext getContext()
+		{
+			return context;
+		}
+	};
+
+	private static final String PAGE_CLASS_NAME = MockPage.class.getName();
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode1()
+	{
+		Url url = Url.parse("wicket/bookmarkable/" + PAGE_CLASS_NAME);
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+		assertEquals(PAGE_CLASS_NAME, page.getClass().getName());
+		assertEquals(0, page.getPageParameters().getIndexedCount());
+		assertTrue(page.getPageParameters().getNamedKeys().isEmpty());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode2()
+	{
+		Url url = Url.parse("wicket/bookmarkable/" + PAGE_CLASS_NAME + "/indexed1?a=b&b=c");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+		assertEquals(PAGE_CLASS_NAME, page.getClass().getName());
+
+		PageParameters p = page.getPageParameters();
+		assertEquals(1, p.getIndexedCount());
+		assertEquals("indexed1", p.get(0).toString());
+
+		assertEquals(2, p.getNamedKeys().size());
+		assertEquals("b", p.get("a").toString());
+		assertEquals("c", p.get("b").toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode3()
+	{
+		Url url = Url.parse("wicket/bookmarkable/" + PAGE_CLASS_NAME + "?15");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+		checkPage(page, 15);
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode4()
+	{
+		Url url = Url.parse("wicket/bookmarkable/" + PAGE_CLASS_NAME + "/i1/i2?15&a=b&b=c");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+		checkPage(page, 15);
+
+		PageParameters p = page.getPageParameters();
+		assertEquals(0, p.getIndexedCount());
+
+		assertEquals(0, p.getNamedKeys().size());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode5()
+	{
+		Url url = Url.parse("wicket/bookmarkable/" + PAGE_CLASS_NAME + "?15-ILinkListener-foo-bar");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof ListenerInterfaceRequestHandler);
+
+		ListenerInterfaceRequestHandler h = (ListenerInterfaceRequestHandler)handler;
+
+		IRequestablePage page = h.getPage();
+		checkPage(page, 15);
+
+		assertEquals(ILinkListener.INTERFACE, h.getListenerInterface());
+		assertEquals("foo:bar", h.getComponent().getPageRelativePath());
+		assertNull(h.getBehaviorIndex());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode6()
+	{
+		Url url = Url.parse("wicket/bookmarkable/" + PAGE_CLASS_NAME +
+			"/i1/i2?15-ILinkListener-foo-bar&a=b&b=c");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof ListenerInterfaceRequestHandler);
+		ListenerInterfaceRequestHandler h = (ListenerInterfaceRequestHandler)handler;
+
+		IRequestablePage page = h.getPage();
+		checkPage(page, 15);
+
+		assertEquals(ILinkListener.INTERFACE, h.getListenerInterface());
+		assertEquals("foo:bar", h.getComponent().getPageRelativePath());
+
+		PageParameters p = page.getPageParameters();
+		assertEquals(0, p.getIndexedCount());
+
+		assertEquals(0, p.getNamedKeys().size());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode7()
+	{
+		Url url = Url.parse("wicket/bookmarkable/" + PAGE_CLASS_NAME +
+			"?15-ILinkListener.4-foo-bar");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof ListenerInterfaceRequestHandler);
+
+		ListenerInterfaceRequestHandler h = (ListenerInterfaceRequestHandler)handler;
+
+		IRequestablePage page = h.getPage();
+		checkPage(page, 15);
+
+		assertEquals(ILinkListener.INTERFACE, h.getListenerInterface());
+		assertEquals("foo:bar", h.getComponent().getPageRelativePath());
+		assertEquals((Object)4, h.getBehaviorIndex());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode8()
+	{
+		Url url = Url.parse("wicket/bookmarkable/" + PAGE_CLASS_NAME +
+			"/i1/i2?15-5.ILinkListener-foo-bar&a=b&b=c");
+
+		context.setNextPageRenderCount(5);
+
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof ListenerInterfaceRequestHandler);
+		ListenerInterfaceRequestHandler h = (ListenerInterfaceRequestHandler)handler;
+
+		IRequestablePage page = h.getPage();
+		assertEquals(page.getRenderCount(), 5);
+	}
+
+	/**
+	 * 
+	 */
+	@Test(expected = StalePageException.class)
+	public void decode9()
+	{
+		Url url = Url.parse("wicket/bookmarkable/" + PAGE_CLASS_NAME +
+			"/i1/i2?15-5.ILinkListener-foo-bar&a=b&b=c");
+
+		context.setNextPageRenderCount(6);
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		((IPageRequestHandler)handler).getPage();
+	}
+
+	/**
+	 * WICKET-2993
+	 */
+	@Test
+	public void decode10()
+	{
+		// use String.class but any other non-Page will do the job as well
+		Url url = Url.parse("wicket/bookmarkable/" + String.class.getName());
+
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertNull("A non-page class should not create a request handler!", handler);
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode1()
+	{
+		PageProvider provider = new PageProvider(MockPage.class, new PageParameters());
+		provider.setPageSource(context);
+		IRequestHandler handler = new BookmarkablePageRequestHandler(provider);
+		Url url = encoder.mapHandler(handler);
+		assertEquals("wicket/bookmarkable/" + PAGE_CLASS_NAME, url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode2()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "i1");
+		parameters.set(1, "i2");
+		parameters.set("a", "b");
+		parameters.set("b", "c");
+		PageProvider provider = new PageProvider(MockPage.class, parameters);
+		provider.setPageSource(context);
+		IRequestHandler handler = new BookmarkablePageRequestHandler(provider);
+		Url url = encoder.mapHandler(handler);
+		assertEquals("wicket/bookmarkable/" + PAGE_CLASS_NAME + "/i1/i2?a=b&b=c", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode3()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "i1");
+		parameters.set(1, "i2");
+		parameters.set("a", "b");
+		parameters.set("b", "c");
+
+		PageProvider provider = new PageProvider(MockPage.class, parameters);
+		provider.setPageSource(context);
+		IRequestHandler handler = new BookmarkablePageRequestHandler(provider);
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("wicket/bookmarkable/" + PAGE_CLASS_NAME + "/i1/i2?a=b&b=c", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode4()
+	{
+		MockPage page = new MockPage(15);
+		page.getPageParameters().set(0, "i1");
+		page.getPageParameters().set(1, "i2");
+		page.getPageParameters().set("a", "b");
+		page.getPageParameters().set("b", "c");
+		page.setCreatedBookmarkable(true);
+
+		IPageProvider provider = new PageProvider(page);
+		IRequestHandler handler = new RenderPageRequestHandler(provider);
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("wicket/bookmarkable/" + PAGE_CLASS_NAME + "/i1/i2?15&a=b&b=c", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode5()
+	{
+		MockPage page = new MockPage(15);
+		page.getPageParameters().set(0, "i1");
+		page.getPageParameters().set(1, "i2");
+		page.getPageParameters().set("a", "b");
+		page.getPageParameters().set("b", "c");
+
+		page.setCreatedBookmarkable(false);
+
+		IPageProvider provider = new PageProvider(page);
+		IRequestHandler handler = new RenderPageRequestHandler(provider);
+		Url url = encoder.mapHandler(handler);
+
+		// never allow bookmarkable render url for page that has not been created by bookmarkable
+		// URL
+
+		assertNull(url);
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode6()
+	{
+		MockPage page = new MockPage(15);
+		page.getPageParameters().set(0, "i1");
+		page.getPageParameters().set(1, "i2");
+		page.getPageParameters().set("a", "b");
+		page.getPageParameters().set("b", "c");
+
+		// shouldn't make any difference for BookmarkableListenerInterfaceRequestHandler,
+		// as this explicitely says the url must be bookmarkable
+		page.setCreatedBookmarkable(false);
+
+		IRequestableComponent c = page.get("foo:bar");
+
+		PageAndComponentProvider provider = new PageAndComponentProvider(page, c);
+		IRequestHandler handler = new BookmarkableListenerInterfaceRequestHandler(provider,
+			ILinkListener.INTERFACE);
+
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("wicket/bookmarkable/" + PAGE_CLASS_NAME +
+			"/i1/i2?15-0.ILinkListener-foo-bar&a=b&b=c", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode7()
+	{
+		MockPage page = new MockPage(15);
+		page.getPageParameters().set(0, "i1");
+		page.getPageParameters().set(1, "i2");
+		page.getPageParameters().set("a", "b");
+		page.getPageParameters().set("b", "c");
+
+		// shouldn't make any difference for BookmarkableListenerInterfaceRequestHandler,
+		// as this explicitely says the url must be bookmarkable
+		page.setCreatedBookmarkable(false);
+
+		IRequestableComponent c = page.get("foo:bar");
+
+		PageAndComponentProvider provider = new PageAndComponentProvider(page, c);
+		IRequestHandler handler = new BookmarkableListenerInterfaceRequestHandler(provider,
+			ILinkListener.INTERFACE, 4);
+
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("wicket/bookmarkable/" + PAGE_CLASS_NAME +
+			"/i1/i2?15-0.ILinkListener.4-foo-bar&a=b&b=c", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode8()
+	{
+		MockPage page = new MockPage(15);
+		page.setBookmarkable(true);
+		page.setCreatedBookmarkable(true);
+		page.setPageStateless(true);
+
+		IPageProvider provider = new PageProvider(page);
+		IRequestHandler handler = new RenderPageRequestHandler(provider);
+
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("wicket/bookmarkable/" + PAGE_CLASS_NAME, url.toString());
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/53f07873/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/CryptoMapperTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/CryptoMapperTest.java b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/CryptoMapperTest.java
new file mode 100644
index 0000000..6f6e8c6
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/CryptoMapperTest.java
@@ -0,0 +1,268 @@
+/*
+ * 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 org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.Request;
+import org.apache.wicket.request.Url;
+import org.apache.wicket.core.request.handler.PageProvider;
+import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
+import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.util.string.StringValue;
+import org.apache.wicket.util.tester.DummyHomePage;
+import org.apache.wicket.util.tester.WicketTester;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Tests for {@link CryptoMapper}
+ */
+public class CryptoMapperTest extends AbstractMapperTest
+{
+	/**
+	 * the encrypted version of {@link #EXPECTED_URL}
+	 */
+	private static final String ENCRYPTED_URL = "SnPh82L4Kl4/SnPe4/4Sn8e/nPh75/h8211";
+
+	/**
+	 * The url to encrypt
+	 */
+	private static final Url EXPECTED_URL = Url.parse("a/b/c/d");
+
+	private CryptoMapper mapper;
+
+	private WicketTester tester;
+
+	/**
+	 * Creates the {@link CryptoMapper}
+	 * 
+	 * @throws Exception
+	 */
+	@Override
+	@Before
+	public void before() throws Exception
+	{
+
+		tester = new WicketTester();
+		WebApplication webApplication = tester.getApplication();
+		webApplication.mountPage(EXPECTED_URL.toString(), DummyHomePage.class);
+		mapper = new CryptoMapper(webApplication.getRootRequestMapper(), webApplication);
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	@After
+	public void after() throws Exception
+	{
+		tester.destroy();
+	}
+
+	/**
+	 * Tests that {@link CryptoMapper} wraps the original request mapper and encrypts the url
+	 * produced by it
+	 */
+	@Test
+	public void encrypt()
+	{
+		Url url = mapper.mapHandler(new RenderPageRequestHandler(new PageProvider(
+			DummyHomePage.class, new PageParameters())));
+		assertEquals(ENCRYPTED_URL, url.toString());
+	}
+
+	/**
+	 * Tests that {@link CryptoMapper} decrypts the passed url and pass it to the original request
+	 * mapper which resolves the page from the application mounts
+	 */
+	@Test
+	public void decrypt()
+	{
+		Request request = getRequest(Url.parse(ENCRYPTED_URL));
+		IRequestHandler requestHandler = mapper.mapRequest(request);
+		assertTrue(requestHandler instanceof RenderPageRequestHandler);
+
+		RenderPageRequestHandler handler = (RenderPageRequestHandler)requestHandler;
+		assertEquals(DummyHomePage.class, handler.getPageClass());
+	}
+
+	/**
+	 * Verifies that the home page can be reached with non-encrypted query parameters.
+	 * https://issues.apache.org/jira/browse/WICKET-4345
+	 */
+	@Test
+	public void decryptHomePageWithNonEncryptedQueryParameters()
+	{
+		Request request = getRequest(Url.parse("?named1=value1"));
+		IRequestHandler requestHandler = mapper.mapRequest(request);
+		assertTrue(requestHandler instanceof RenderPageRequestHandler);
+
+		RenderPageRequestHandler handler = (RenderPageRequestHandler)requestHandler;
+		assertEquals(tester.getApplication().getHomePage(), handler.getPageClass());
+		StringValue queryParam = handler.getPageParameters().get("named1");
+		assertEquals("value1", queryParam.toOptionalString());
+	}
+
+	/**
+	 * Test a failed decrypt, WICKET-4139
+	 */
+	@Test
+	public void decryptFailed()
+	{
+		String encrypted = "style.css";
+
+		Request request = getRequest(Url.parse(encrypted));
+
+		assertNull(mapper.mapRequest(request));
+	}
+
+	/**
+	 * Tests that named and indexed parameters are properly (en|de)crypted
+	 */
+	@Test
+	public void pageParameters()
+	{
+		String expectedEncrypted = "ywKWg-Qpk7YQBiYCmj9MaAJSIV1gtssNinjiALijtet62VMQc2-sMK_RchttkidUpYM_cplXKeZSfGxBkvWzH_E_zWv4Ii7MNSm5nXKno7o/ywK6c/MK_c0/nji3c/Qpk1b/XKnba/c2-cd";
+
+		PageParameters expectedParameters = new PageParameters();
+		expectedParameters.add("namedKey1", "namedValue1");
+		expectedParameters.add("namedKey2", "namedValue2");
+		expectedParameters.set(0, "indexedValue1");
+		expectedParameters.set(1, "indexedValue2");
+		RenderPageRequestHandler renderPageRequestHandler = new RenderPageRequestHandler(
+			new PageProvider(DummyHomePage.class, expectedParameters));
+		Url url = mapper.mapHandler(renderPageRequestHandler);
+// System.err.println(url.toString());
+		assertEquals(expectedEncrypted, url.toString());
+
+		Request request = getRequest(url);
+		IRequestHandler requestHandler = mapper.mapRequest(request);
+		assertTrue(requestHandler instanceof RenderPageRequestHandler);
+
+		RenderPageRequestHandler handler = (RenderPageRequestHandler)requestHandler;
+		assertEquals(DummyHomePage.class, handler.getPageClass());
+		PageParameters actualParameters = handler.getPageParameters();
+		assertEquals(expectedParameters, actualParameters);
+	}
+
+	/**
+	 * https://issues.apache.org/jira/browse/WICKET-3926
+	 */
+	@Test
+	public void homePageWithParameters()
+	{
+		String expectedEncrypted = "0lhSFdMIt3yZUNwbtLuXgDePMclxSbks";
+		PageParameters expectedParameters = new PageParameters();
+		expectedParameters.add("namedKey1", "namedValue1");
+
+		RenderPageRequestHandler renderPageRequestHandler = new RenderPageRequestHandler(
+			new PageProvider(tester.getApplication().getHomePage(), expectedParameters));
+		Url url = mapper.mapHandler(renderPageRequestHandler);
+		assertEquals(expectedEncrypted, url.toString());
+
+		Request request = getRequest(url);
+		IRequestHandler requestHandler = mapper.mapRequest(request);
+		assertTrue(requestHandler instanceof RenderPageRequestHandler);
+
+		RenderPageRequestHandler handler = (RenderPageRequestHandler)requestHandler;
+		assertEquals(tester.getApplication().getHomePage(), handler.getPageClass());
+		PageParameters actualParameters = handler.getPageParameters();
+		assertEquals(expectedParameters, actualParameters);
+	}
+
+	/**
+	 * Relative ResourceReferences, WICKET-3514
+	 */
+	@Test
+	public void resourceReference()
+	{
+		String encrypted = "X5EA-RpmG5-t7GSByiSposVVWJ28fpoU-XgFo7bOPISxb3xq2Cs66Z2lkUjUYqOQlzEia56fViCD0yNMzA9ySE9DRAA5J3OUWSCSO3B8FjFYPHWdkqgcHg/X5E87/kUj7f/B8F3f/yiSe2/UYq6c";
+
+		Request request = getRequest(Url.parse(encrypted));
+
+		IRequestHandler requestHandler = mapper.mapRequest(request);
+
+		assertTrue(requestHandler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler handler = (ResourceReferenceRequestHandler)requestHandler;
+
+		assertEquals(getClass(), handler.getResourceReference().getScope());
+		assertEquals("crypt/crypt.txt", handler.getResourceReference().getName());
+	}
+
+	/**
+	 * Relative ResourceReferences, WICKET-3514
+	 */
+	@Test
+	@Ignore // TODO @svenmeier: How to regenerate the encrypted url ?
+	public void resourceReferenceWithModifiedSegments()
+	{
+		String encrypted = "X5EA-RpmG5-t7GSByiSposVVWJ28fpoU-XgFo7bOPITjbCTT6mLI5l-7b-WJucu-Kc8StVsu-PL5htkbIxuxphv3mYi5-mmkCvkxPsriihj5VPg3naw2fA/X5E87/b-W6b/l-795/Juc97/modified-crypt.txt";
+
+		Request request = getRequest(Url.parse(encrypted));
+
+		IRequestHandler requestHandler = mapper.mapRequest(request);
+
+		assertTrue(requestHandler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler handler = (ResourceReferenceRequestHandler)requestHandler;
+
+		assertEquals(getClass(), handler.getResourceReference().getScope());
+		assertEquals("crypt/modified-crypt.txt", handler.getResourceReference().getName());
+	}
+
+	/**
+	 * Relative ResourceReferences, WICKET-3514
+	 */
+	@Test
+	@Ignore // TODO @svenmeier: How to regenerate the encrypted url ?
+	public void resourceReferenceWithMoreSegments()
+	{
+		String encrypted = "X5EA-RpmG5-t7GSByiSposVVWJ28fpoU-XgFo7bOPITjbCTT6mLI5l-7b-WJucu-Kc8StVsu-PL5htkbIxuxphv3mYi5-mmkCvkxPsriihj5VPg3naw2fA/X5E87/b-W6b/l-795/Juc97/more/crypt.txt";
+
+		Request request = getRequest(Url.parse(encrypted));
+
+		IRequestHandler requestHandler = mapper.mapRequest(request);
+
+		assertTrue(requestHandler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler handler = (ResourceReferenceRequestHandler)requestHandler;
+
+		assertEquals(getClass(), handler.getResourceReference().getScope());
+		assertEquals("crypt/more/crypt.txt", handler.getResourceReference().getName());
+	}
+
+	/**
+	 * Relative ResourceReferences, WICKET-3514
+	 */
+	@Test
+	@Ignore // TODO @svenmeier: How to regenerate the encrypted url ?
+	public void resourceReferenceWithLessSegments()
+	{
+		String encrypted = "X5EA-RpmG5-t7GSByiSposVVWJ28fpoU-XgFo7bOPITjbCTT6mLI5l-7b-WJucu-Kc8StVsu-PL5htkbIxuxphv3mYi5-mmkCvkxPsriihj5VPg3naw2fA/X5E87/b-W6b/l-795/less-crypt.txt";
+
+		Request request = getRequest(Url.parse(encrypted));
+
+		IRequestHandler requestHandler = mapper.mapRequest(request);
+
+		assertTrue(requestHandler instanceof ResourceReferenceRequestHandler);
+		ResourceReferenceRequestHandler handler = (ResourceReferenceRequestHandler)requestHandler;
+
+		assertEquals(getClass(), handler.getResourceReference().getScope());
+		assertEquals("less-crypt.txt", handler.getResourceReference().getName());
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/53f07873/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/MountedMapperTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/MountedMapperTest.java b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/MountedMapperTest.java
new file mode 100644
index 0000000..c52fa9a
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/MountedMapperTest.java
@@ -0,0 +1,787 @@
+/*
+ * 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 org.apache.wicket.MockPage;
+import org.apache.wicket.core.request.handler.BookmarkableListenerInterfaceRequestHandler;
+import org.apache.wicket.markup.html.link.ILinkListener;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.IRequestMapper;
+import org.apache.wicket.request.Url;
+import org.apache.wicket.request.component.IRequestableComponent;
+import org.apache.wicket.request.component.IRequestablePage;
+import org.apache.wicket.core.request.handler.BookmarkablePageRequestHandler;
+import org.apache.wicket.core.request.handler.IPageProvider;
+import org.apache.wicket.core.request.handler.IPageRequestHandler;
+import org.apache.wicket.core.request.handler.ListenerInterfaceRequestHandler;
+import org.apache.wicket.core.request.handler.PageAndComponentProvider;
+import org.apache.wicket.core.request.handler.PageProvider;
+import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
+import org.apache.wicket.request.http.WebRequest;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.junit.Test;
+
+/**
+ * @author Matej Knopp
+ */
+public class MountedMapperTest extends AbstractMapperTest
+{
+
+	/**
+	 * Construct.
+	 */
+	public MountedMapperTest()
+	{
+	}
+
+	private final MountedMapper encoder = new MountedMapper("/some/mount/path", MockPage.class)
+	{
+		@Override
+		protected IMapperContext getContext()
+		{
+			return context;
+		}
+
+		@Override
+		boolean getRecreateMountedPagesAfterExpiry()
+		{
+			return true;
+		}
+	};
+
+	private final MountedMapper placeholderEncoder = new MountedMapper(
+		"/some/${param1}/path/${param2}", MockPage.class)
+	{
+		@Override
+		protected IMapperContext getContext()
+		{
+			return context;
+		}
+
+		@Override
+		boolean getRecreateMountedPagesAfterExpiry()
+		{
+			return true;
+		}
+	};
+
+	private final MountedMapper optionPlaceholderEncoder = new MountedMapper(
+		"/some/#{param1}/path/${param2}/#{param3}", MockPage.class)
+	{
+		@Override
+		protected IMapperContext getContext()
+		{
+			return context;
+		}
+
+		@Override
+		boolean getRecreateMountedPagesAfterExpiry()
+		{
+			return true;
+		}
+	};
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode1()
+	{
+		Url url = Url.parse("some/mount/path");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+		assertEquals(0, page.getPageParameters().getIndexedCount());
+		assertTrue(page.getPageParameters().getNamedKeys().isEmpty());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode2()
+	{
+		Url url = Url.parse("some/mount/path/indexed1?a=b&b=c");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+		PageParameters p = page.getPageParameters();
+		assertEquals(1, p.getIndexedCount());
+		assertEquals("indexed1", p.get(0).toString());
+
+		assertEquals(2, p.getNamedKeys().size());
+		assertEquals("b", p.get("a").toString());
+		assertEquals("c", p.get("b").toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode3()
+	{
+		Url url = Url.parse("some/mount/path?15");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+		checkPage(page, 15);
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode4()
+	{
+		Url url = Url.parse("some/mount/path/i1/i2?15&a=b&b=c");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+		checkPage(page, 15);
+
+		PageParameters p = page.getPageParameters();
+		assertEquals(0, p.getIndexedCount());
+
+		assertEquals(0, p.getNamedKeys().size());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode5()
+	{
+		Url url = Url.parse("some/mount/path?15-ILinkListener-foo-bar");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof ListenerInterfaceRequestHandler);
+
+		ListenerInterfaceRequestHandler h = (ListenerInterfaceRequestHandler)handler;
+
+		IRequestablePage page = h.getPage();
+		checkPage(page, 15);
+
+		assertEquals(ILinkListener.INTERFACE, h.getListenerInterface());
+		assertEquals("foo:bar", h.getComponent().getPageRelativePath());
+		assertNull(h.getBehaviorIndex());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode6()
+	{
+		Url url = Url.parse("some/mount/path/i1/i2?15-ILinkListener-foo-bar&a=b&b=c");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof ListenerInterfaceRequestHandler);
+		ListenerInterfaceRequestHandler h = (ListenerInterfaceRequestHandler)handler;
+
+		IRequestablePage page = h.getPage();
+		checkPage(page, 15);
+
+		assertEquals(ILinkListener.INTERFACE, h.getListenerInterface());
+		assertEquals("foo:bar", h.getComponent().getPageRelativePath());
+
+		PageParameters p = page.getPageParameters();
+		assertEquals(0, p.getIndexedCount());
+
+		assertEquals(0, p.getNamedKeys().size());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode7()
+	{
+		Url url = Url.parse("some/mount/path?param1=value1&15-ILinkListener.4-foo-bar");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof ListenerInterfaceRequestHandler);
+
+		ListenerInterfaceRequestHandler h = (ListenerInterfaceRequestHandler)handler;
+
+		IRequestablePage page = h.getPage();
+		checkPage(page, 15);
+
+		assertEquals(ILinkListener.INTERFACE, h.getListenerInterface());
+		assertEquals("foo:bar", h.getComponent().getPageRelativePath());
+		assertEquals((Object)4, h.getBehaviorIndex());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode8()
+	{
+		Url url = Url.parse("some/mmount/path?15-ILinkListener.4-foo-bar");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertNull(handler);
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode9()
+	{
+		// capture the home page
+		Url url = Url.parse("");
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+		assertTrue(handler instanceof RenderPageRequestHandler);
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void decode10()
+	{
+		Url url = Url.parse("some/mount/path?15-5.ILinkListener.4-foo-bar");
+		context.setNextPageRenderCount(5);
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof ListenerInterfaceRequestHandler);
+
+		ListenerInterfaceRequestHandler h = (ListenerInterfaceRequestHandler)handler;
+
+		IRequestablePage page = h.getPage();
+		assertEquals(5, page.getRenderCount());
+	}
+
+	/**
+	 * 
+	 */
+	@Test(expected = StalePageException.class)
+	public void decode11()
+	{
+		Url url = Url.parse("some/mount/path?15-5.ILinkListener.4-foo-bar");
+		context.setNextPageRenderCount(7);
+
+		IRequestHandler handler = encoder.mapRequest(getRequest(url));
+
+		((IPageRequestHandler)handler).getPage();
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode1()
+	{
+		PageProvider provider = new PageProvider(MockPage.class, new PageParameters());
+		provider.setPageSource(context);
+		IRequestHandler handler = new BookmarkablePageRequestHandler(provider);
+		Url url = encoder.mapHandler(handler);
+		assertEquals("some/mount/path", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode2()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "i1");
+		parameters.set(1, "i2");
+		parameters.set("a", "b");
+		parameters.set("b", "c");
+		PageProvider provider = new PageProvider(MockPage.class, parameters);
+		provider.setPageSource(context);
+		IRequestHandler handler = new BookmarkablePageRequestHandler(provider);
+		Url url = encoder.mapHandler(handler);
+		assertEquals("some/mount/path/i1/i2?a=b&b=c", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode3()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "i1");
+		parameters.set(1, "i2");
+		parameters.set("a", "b");
+		parameters.set("b", "c");
+
+		PageProvider provider = new PageProvider(MockPage.class, parameters);
+		provider.setPageSource(context);
+		IRequestHandler handler = new BookmarkablePageRequestHandler(provider);
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("some/mount/path/i1/i2?a=b&b=c", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode4()
+	{
+		MockPage page = new MockPage(15);
+		page.getPageParameters().set(0, "i1");
+		page.getPageParameters().set(1, "i2");
+		page.getPageParameters().set("a", "b");
+		page.getPageParameters().set("b", "c");
+		page.setCreatedBookmarkable(true);
+
+		IPageProvider provider = new PageProvider(page);
+		IRequestHandler handler = new RenderPageRequestHandler(provider);
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("some/mount/path/i1/i2?15&a=b&b=c", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode5()
+	{
+		MockPage page = new MockPage(15);
+		page.getPageParameters().set(0, "i1");
+		page.getPageParameters().set(1, "i2");
+		page.getPageParameters().set("a", "b");
+		page.getPageParameters().set("b", "c");
+
+		page.setCreatedBookmarkable(false);
+
+		IPageProvider provider = new PageProvider(page);
+		IRequestHandler handler = new RenderPageRequestHandler(provider);
+		Url url = encoder.mapHandler(handler);
+
+		// mounted pages must render mounted url even for page that has not been created by
+		// bookmarkable
+		// URL
+
+		assertEquals("some/mount/path/i1/i2?15&a=b&b=c", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode6()
+	{
+		MockPage page = new MockPage(15);
+		page.getPageParameters().set(0, "i1");
+		page.getPageParameters().set(1, "i2");
+		page.getPageParameters().set("a", "b");
+		page.getPageParameters().set("b", "c");
+		page.setRenderCount(4);
+
+		// shouldn't make any difference for BookmarkableListenerInterfaceRequestHandler,
+		// as this explicitely says the url must be bookmarkable
+		page.setCreatedBookmarkable(false);
+
+		IRequestableComponent c = page.get("foo:bar");
+
+		PageAndComponentProvider provider = new PageAndComponentProvider(page, c);
+		IRequestHandler handler = new BookmarkableListenerInterfaceRequestHandler(provider,
+			ILinkListener.INTERFACE);
+
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("some/mount/path/i1/i2?15-4.ILinkListener-foo-bar&a=b&b=c", url.toString());
+	}
+
+	/**
+	 * https://issues.apache.org/jira/browse/WICKET-4014
+	 * 
+	 * The difference with testEncode7 is that here ListenerInterfaceRequestHandler is used instead
+	 * of BookmarkableListenerInterfaceRequestHandler
+	 */
+	@Test
+	public void encode6_1()
+	{
+		MockPage page = new MockPage(15);
+		page.getPageParameters().set(0, "i1");
+		page.getPageParameters().set(1, "i2");
+		page.getPageParameters().set("a", "b");
+		page.getPageParameters().set("b", "c");
+
+		// WICKET-4038
+		page.getPageParameters().add(WebRequest.PARAM_AJAX, "true");
+		page.getPageParameters().add(WebRequest.PARAM_AJAX_BASE_URL, "some/base/url");
+
+		page.setRenderCount(4);
+
+		// shouldn't make any difference for ListenerInterfaceRequestHandler,
+		// as this explicitely says the url must be bookmarkable
+		page.setCreatedBookmarkable(false);
+
+		IRequestableComponent c = page.get("foo:bar");
+
+		PageAndComponentProvider provider = new PageAndComponentProvider(page, c);
+		IRequestHandler handler = new ListenerInterfaceRequestHandler(provider,
+			ILinkListener.INTERFACE);
+
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("some/mount/path/i1/i2?15-4.ILinkListener-foo-bar&a=b&b=c", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode7()
+	{
+		MockPage page = new MockPage(15);
+		page.getPageParameters().set(0, "i1");
+		page.getPageParameters().set(1, "i2");
+		page.getPageParameters().set("a", "b");
+		page.getPageParameters().set("b", "c");
+		page.setRenderCount(5);
+
+		// shouldn't make any difference for BookmarkableListenerInterfaceRequestHandler,
+		// as this explicitely says the url must be bookmarkable
+		page.setCreatedBookmarkable(false);
+
+		IRequestableComponent c = page.get("foo:bar");
+
+		PageAndComponentProvider provider = new PageAndComponentProvider(page, c);
+		IRequestHandler handler = new BookmarkableListenerInterfaceRequestHandler(provider,
+			ILinkListener.INTERFACE, 4);
+
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("some/mount/path/i1/i2?15-5.ILinkListener.4-foo-bar&a=b&b=c", url.toString());
+	}
+
+	/**
+	 * https://issues.apache.org/jira/browse/WICKET-4014
+	 * 
+	 * The difference with testEncode7 is that here ListenerInterfaceRequestHandler is used instead
+	 * of BookmarkableListenerInterfaceRequestHandler
+	 */
+	@Test
+	public void encode7_1()
+	{
+		MockPage page = new MockPage(15);
+		page.getPageParameters().set(0, "i1");
+		page.getPageParameters().set(1, "i2");
+		page.getPageParameters().set("a", "b");
+		page.getPageParameters().set("b", "c");
+		page.setRenderCount(5);
+
+		// shouldn't make any difference for ListenerInterfaceRequestHandler,
+		// as this explicitely says the url must be bookmarkable
+		page.setCreatedBookmarkable(false);
+
+		IRequestableComponent c = page.get("foo:bar");
+
+		PageAndComponentProvider provider = new PageAndComponentProvider(page, c);
+		IRequestHandler handler = new ListenerInterfaceRequestHandler(provider,
+			ILinkListener.INTERFACE, 4);
+
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("some/mount/path/i1/i2?15-5.ILinkListener.4-foo-bar&a=b&b=c", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void encode8()
+	{
+		MockPage page = new MockPage(15);
+		page.setBookmarkable(true);
+		page.setCreatedBookmarkable(true);
+		page.setPageStateless(true);
+
+		IPageProvider provider = new PageProvider(page);
+		IRequestHandler handler = new RenderPageRequestHandler(provider);
+
+		Url url = encoder.mapHandler(handler);
+
+		assertEquals("some/mount/path", url.toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test(expected = IllegalArgumentException.class)
+	public void construct1()
+	{
+		IRequestMapper e = new MountedMapper("", MockPage.class);
+	}
+
+	/**
+	 * Overriding mounting on '/' (see HomePageMapper) should be possible
+	 */
+	@Test
+	public void construct2()
+	{
+		IRequestMapper homePageMapper = new MountedMapper("/", MockPage.class);
+		assertNotNull(homePageMapper);
+	}
+
+	/**
+	 *
+	 */
+	@Test
+	public void placeholderDecode1()
+	{
+		Url url = Url.parse("some/p1/path/p2");
+		IRequestHandler handler = placeholderEncoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+		assertEquals(0, page.getPageParameters().getIndexedCount());
+		assertTrue(page.getPageParameters().getNamedKeys().size() == 2);
+		assertEquals("p1", page.getPageParameters().get("param1").toString());
+		assertEquals("p2", page.getPageParameters().get("param2").toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void placeholderDecode2()
+	{
+		Url url = Url.parse("some/p1/path/p2/indexed1?a=b&b=c");
+		IRequestHandler handler = placeholderEncoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+		PageParameters p = page.getPageParameters();
+		assertEquals(1, p.getIndexedCount());
+		assertEquals("indexed1", p.get(0).toString());
+
+		assertEquals(4, p.getNamedKeys().size());
+		assertEquals("b", p.get("a").toString());
+		assertEquals("c", p.get("b").toString());
+		assertEquals("p1", page.getPageParameters().get("param1").toString());
+		assertEquals("p2", page.getPageParameters().get("param2").toString());
+	}
+
+	/**	 */
+	@Test
+	public void placeholderDecodeWithIndexedParameters()
+	{
+		Url url = Url.parse("some/p1/path/p2/p3/p4");
+		IRequestHandler handler = placeholderEncoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+		assertEquals(2, page.getPageParameters().getIndexedCount());
+		assertTrue(page.getPageParameters().getNamedKeys().size() == 2);
+		assertEquals("p1", page.getPageParameters().get("param1").toString());
+		assertEquals("p2", page.getPageParameters().get("param2").toString());
+		assertEquals("p3", page.getPageParameters().get(0).toString());
+		assertEquals("p4", page.getPageParameters().get(1).toString());
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void placeholderEncode2()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "i1");
+		parameters.set(1, "i2");
+		parameters.set("a", "b");
+		parameters.set("b", "c");
+		parameters.set("param1", "p1");
+		parameters.set("param2", "p2");
+
+
+		PageProvider provider = new PageProvider(MockPage.class, parameters);
+		provider.setPageSource(context);
+		IRequestHandler handler = new BookmarkablePageRequestHandler(provider);
+		Url url = placeholderEncoder.mapHandler(handler);
+		assertEquals("some/p1/path/p2/i1/i2?a=b&b=c", url.toString());
+	}
+
+	/**
+	 * Test Url creation with {@link RenderPageRequestHandler}. Cheat that the page instance is not
+	 * new, this way the produced Url has version '1' in the page info parameter
+	 */
+	@Test
+	public void placeholderEncode3()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "i1");
+		parameters.set(1, "i2");
+		parameters.set("a", "b");
+		parameters.set("b", "c");
+		parameters.set("param1", "p1");
+		parameters.set("param2", "p2");
+
+		PageProvider provider = new PageProvider(MockPage.class, parameters)
+		{
+			@Override
+			public boolean isNewPageInstance()
+			{
+				return false;
+			}
+		};
+		provider.setPageSource(context);
+		IRequestHandler handler = new RenderPageRequestHandler(provider);
+		Url url = placeholderEncoder.mapHandler(handler);
+		assertEquals("some/p1/path/p2/i1/i2?1&a=b&b=c", url.toString());
+	}
+
+	/** */
+	@Test
+	public void optionPlaceholderDecode1()
+	{
+		Url url = Url.parse("some/p1/path/p2/p3");
+		IRequestHandler handler = optionPlaceholderEncoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+		assertEquals(0, page.getPageParameters().getIndexedCount());
+		assertTrue(page.getPageParameters().getNamedKeys().size() == 3);
+		assertEquals("p1", page.getPageParameters().get("param1").toString());
+		assertEquals("p2", page.getPageParameters().get("param2").toString());
+		assertEquals("p3", page.getPageParameters().get("param3").toString());
+	}
+
+	/** */
+	@Test
+	public void optionPlaceholderDecodeEagerMatchParameters()
+	{
+		Url url = Url.parse("some/path/path/path");
+		IRequestHandler handler = optionPlaceholderEncoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+		assertEquals(0, page.getPageParameters().getIndexedCount());
+		assertTrue(page.getPageParameters().getNamedKeys().size() == 2);
+		assertEquals("path", page.getPageParameters().get("param1").toString());
+		assertEquals("path", page.getPageParameters().get("param2").toString());
+		assertFalse("param3 should not be set",
+			page.getPageParameters().getNamedKeys().contains("param3"));
+	}
+
+	/** */
+	@Test
+	public void optionPlaceholderDecode2()
+	{
+		Url url = Url.parse("some/p1/path/p2");
+		IRequestHandler handler = optionPlaceholderEncoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+		assertEquals(0, page.getPageParameters().getIndexedCount());
+		assertTrue(page.getPageParameters().getNamedKeys().size() == 2);
+		assertEquals("p1", page.getPageParameters().get("param1").toString());
+		assertEquals("p2", page.getPageParameters().get("param2").toString());
+		assertFalse("param3 should not be set",
+			page.getPageParameters().getNamedKeys().contains("param3"));
+	}
+
+	/** */
+	@Test
+	public void optionPlaceholderDecode3()
+	{
+		Url url = Url.parse("some/path/p2");
+		IRequestHandler handler = optionPlaceholderEncoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+		assertEquals(0, page.getPageParameters().getIndexedCount());
+		assertTrue(page.getPageParameters().getNamedKeys().size() == 1);
+		assertFalse("param1 should not be set",
+			page.getPageParameters().getNamedKeys().contains("param1"));
+		assertEquals("p2", page.getPageParameters().get("param2").toString());
+		assertFalse("param3 should not be set",
+			page.getPageParameters().getNamedKeys().contains("param3"));
+	}
+
+	/** */
+	@Test
+	public void optionPlaceholderDecodeWithIndexParams()
+	{
+		Url url = Url.parse("some/path/p2/p3/p4");
+		IRequestHandler handler = optionPlaceholderEncoder.mapRequest(getRequest(url));
+
+		assertTrue(handler instanceof RenderPageRequestHandler);
+		IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+		assertEquals(1, page.getPageParameters().getIndexedCount());
+		assertTrue(page.getPageParameters().getNamedKeys().size() == 2);
+		assertFalse("param1 should not be set",
+			page.getPageParameters().getNamedKeys().contains("param1"));
+		assertEquals("p2", page.getPageParameters().get("param2").toString());
+		assertEquals("p3", page.getPageParameters().get("param3").toString());
+		assertEquals("p4", page.getPageParameters().get(0).toString());
+	}
+
+	/** */
+	@Test
+	public void optionPlaceholderEncode1()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "i1");
+		parameters.set(1, "i2");
+		parameters.set("a", "b");
+		parameters.set("b", "c");
+		parameters.set("param1", "p1");
+		parameters.set("param2", "p2");
+
+
+		PageProvider provider = new PageProvider(MockPage.class, parameters);
+		provider.setPageSource(context);
+		IRequestHandler handler = new BookmarkablePageRequestHandler(provider);
+		Url url = optionPlaceholderEncoder.mapHandler(handler);
+		assertEquals("some/p1/path/p2/i1/i2?a=b&b=c", url.toString());
+	}
+
+	/** */
+	@Test
+	public void optionPlaceholderEncode2()
+	{
+		PageParameters parameters = new PageParameters();
+		parameters.set(0, "i1");
+		parameters.set(1, "i2");
+		parameters.set("a", "b");
+		parameters.set("b", "c");
+		parameters.set("param2", "p2");
+		parameters.set("param3", "p3");
+
+
+		PageProvider provider = new PageProvider(MockPage.class, parameters);
+		provider.setPageSource(context);
+		IRequestHandler handler = new BookmarkablePageRequestHandler(provider);
+		Url url = optionPlaceholderEncoder.mapHandler(handler);
+		assertEquals("some/path/p2/p3/i1/i2?a=b&b=c", url.toString());
+	}
+}