A topic that crops up time and time again is that of the elusive HTTP
404 in T5 apps.

When running a public website, for SEO reasons, it is desirable for
in-exact urls to return a HTTP 404 rather than returning a 'best fit'
page.For the same reasons, it is undesirable for malformed urls to
return an HTTP 500. So in T5.3.4, I finally got round to converting
all those URL requests to HTTP 404s.

The approach taken is to generate a 'MangledUrlException' at poignant
points and have an ExceptionHandler catch this specific exception to
return a HTTP 404.The code changes I use to throw a
'MangledUrlException' are outlined below:

My method of catching and identifying the MangledUrlException (as it's
often wrapped deep inside a T5 exception) may not be suitable for all,
so I've left that part out.



Use Case 1 : Illegal Url "http://localhost/spa%20ce";
This usually gives a java.lang.IllegalArgumentException:
 - Input string 'spa ce' is not valid; the character ' ' at position 4
is not valid.

To your module add:

  @Advise(serviceInterface=ContextPathEncoder.class) @Traditional
  public static void adviseContextPathEncoder(MethodAdviceReceiver
receiver, @Autobuild AdviseContextPathEncoder advice) throws
SecurityException, NoSuchMethodException {
    Method method = ContextPathEncoder.class.getMethod("decodePath",
String.class);
    receiver.adviseMethod(method, advice);
  }

  public class AdviseContextPathEncoder implements MethodAdvice {
    @Override
    public void advise(MethodInvocation invocation) {
      try {
        invocation.proceed();
      } catch (IllegalArgumentException e) {
        throw new MangledUrlException();
      }
    }
  }



Use Case 2 : Illegal Url "http://localhost/index.wotever";
This usually gives an org.apache.tapestry5.ioc.util.UnknownValueException
 - Component Index does not contain embedded component 'wotever'.

  @Advise(serviceInterface=ComponentEventRequestHandler.class)
  public static void
adviseComponentEventRequestHandler(MethodAdviceReceiver receiver,
@Autobuild AdviseComponentEventRequestHandler advice) throws
SecurityException, NoSuchMethodException {
    Method method =
ComponentEventRequestHandler.class.getMethod("handle",
ComponentEventRequestParameters.class);
    receiver.adviseMethod(method, advice);
  }

  public class AdviseComponentEventRequestHandler implements MethodAdvice {
    private final String className;
    private final String methodName;

    public AdviseComponentEventRequestHandler() throws
SecurityException, NoSuchMethodException {
      // set these early so we're notified of any API changes
      // PageImpl isn't a service, so we can't advise it directly!
      className   = PageImpl.class.getName();
      methodName  =
PageImpl.class.getMethod("getComponentElementByNestedId",
String.class).getName();
    }

    @Override
    public void advise(MethodInvocation invocation) {
      try {
        invocation.proceed();
      } catch (UnknownValueException e) {
        if (componentIdIsMangled(e)) {
          ComponentEventRequestParameters params =
(ComponentEventRequestParameters) invocation.getParameter(0);
          throw new MangledUrlException(params.getActivePageName(),
MangledUrlMessages.componentNotFound(params.getNestedComponentId()),
e);
        }
      }
    }

    private boolean componentIdIsMangled(UnknownValueException e) {
      for (StackTraceElement stackElement : e.getStackTrace())
        if (stackElement.getClassName().equals(className) &&
stackElement.getMethodName().equals(methodName))
          return true;
      return false;
    }
  }



Use Case 3 : Unwanted context "http://localhost/index/unwanted";
This usually returns the index page.

  @Contribute(ComponentClassTransformWorker2.class)
  @Primary
  public static void
provideTransformWorkers(OrderedConfiguration<ComponentClassTransformWorker2>
configuration) {
    configuration.addInstance("ContextNotRequiredWorker",
NotRequiredWorker.class, "after:*");
  }

  /**
   * It's important that that this is added "after:*" as we need to
ensure no-one else has added onActivate() handlers.
   */
  public class NotRequiredWorker implements ComponentClassTransformWorker2 {
    @Override
    public void transform(PlasticClass plasticClass,
TransformationSupport support, MutableComponentModel model) {
      if (!model.isPage())
        return;

      // FUTURE: this does not identify mixins which implement onActivate()
      if (model.handlesEvent(EventConstants.ACTIVATE))
        return;

      support.addEventHandler(EventConstants.ACTIVATE, 0,
        "NotRequiredWorker activate event handler",
        new ComponentEventHandler() {
          @Override
          public void handleEvent(Component instance, ComponentEvent event) {
          if (event.getContext().length > 0) {
            String pageName = instance.getComponentResources().getPageName();
            throw new MangledUrlException(pageName,
MangledUrlMessages.contextNotWanted(event.getEventContext().toStrings()));
          }
        }
      });
    }
  }

--
Steve Eynon
-------------------------------
"If at first you don't succeed,
   so much for skydiving!"

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org

Reply via email to