Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
A very hacky workaround for the "Index pages" issue. see
import org.apache.tapestry5.EventContext;
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.TapestryConstants;
import org.apache.tapestry5.internal.EmptyEventContext;
import org.apache.tapestry5.ioc.MethodAdviceReceiver;
import org.apache.tapestry5.ioc.OrderedConfiguration;
import org.apache.tapestry5.ioc.annotations.Advise;
import org.apache.tapestry5.ioc.annotations.Contribute;
import org.apache.tapestry5.ioc.annotations.Primary;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.plastic.MethodInvocation;
public class RoutingHacksModule {
private static final EventContext EMPTY_CONTEXT = new EmptyEventContext();
@Advise(serviceInterface = ComponentEventLinkEncoder.class)
public static void noIndexPages(MethodAdviceReceiver receiver) throws NoSuchMethodException {
receiver.adviseMethod(ComponentEventLinkEncoder.class.getMethod("decodePageRenderRequest", Request.class), new MethodAdvice() {
public void advise(MethodInvocation methodInvocation) {
PageRenderRequestParameters parameters = (PageRenderRequestParameters) methodInvocation.getReturnValue();
if (parameters != null && parameters.getLogicalPageName().toLowerCase().endsWith("index"))
public static void setupCustomDispatcher(OrderedConfiguration<Dispatcher> configuration,
final ComponentRequestHandler componentRequestHandler,
final ComponentClassResolver componentClassResolver,
final @Symbol(SymbolConstants.APPLICATION_FOLDER) String applicationFolder) {
configuration.add("MyRootDispatcher", new Dispatcher() {
public boolean dispatch(Request request, Response response) throws IOException {
// The extended name may include a page activation context. The trick is
// to figure out where the logical page name stops and where the
// activation context begins. Further, strip out the leading slash.
boolean hasAppFolder = applicationFolder.equals("");
String applicationFolderPrefix = hasAppFolder ? null : "/" + applicationFolder;
String path = request.getPath();
if (applicationFolderPrefix != null) {
int prefixLength = applicationFolderPrefix.length();
assert path.substring(0, prefixLength).equalsIgnoreCase(applicationFolderPrefix);
// This checks that the character after the prefix is a slash ... the extra complexity
// only seems to occur in Selenium. There's some ambiguity about what to do with a request for
// the application folder that doesn't end with a slash. Manuyal with Chrome and IE 8 shows that such
// requests are passed through with a training slash, automated testing with Selenium and FireFox
// can include requests for the folder without the trailing slash.
assert path.length() <= prefixLength || path.charAt(prefixLength) == '/';
// Strip off the folder prefix (i.e., "/foldername"), leaving the rest of the path (i.e., "/en/pagename").
path = path.substring(prefixLength);
// TAPESTRY-1343: Sometimes path is the empty string (it should always be at least a slash,
// but Tomcat may return the empty string for a root context request).
String extendedName = path.length() == 0 ? path : path.substring(1);
// Ignore trailing slashes in the path.
while (extendedName.endsWith("/")) {
extendedName = extendedName.substring(0, extendedName.length() - 1);
if (componentClassResolver.isPageName(extendedName)) {
String canonicalized = componentClassResolver.canonicalizePageName(extendedName);
boolean loopback = request.getParameter(TapestryConstants.PAGE_LOOPBACK_PARAMETER_NAME) != null;
PageRenderRequestParameters parameters = new PageRenderRequestParameters(canonicalized, EMPTY_CONTEXT, loopback);
return true;
return false;
}, "after:*");

ascandroli commented Oct 12, 2012

This module accomplishes three things:

  • The default PageRenderDispatcher will skip Index pages.
  • There is a new Dispatcher that will deal with the Index pages but it's configured after all the other dispatchers with "after:*"
  • This new Dispatcher mandates that Index pages MUST NOT have a context. It forces EMPTY_CONTEXT on all the Index pages.

This is really really hacky code, but somehow very useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment