Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Service advice represents a powerful meta-programming facility availble available to services. In fact, it is a kind of limited Aspect Oriented Programming.

Service advice allows you to intercept method invocations on your services; you have the abillity ability to see what methods get invoked, what the parameters are. You can let the normal code do it work, and then inspect or event adjust the return value, or any thrown exceptions. And you can do this all in normal Java code.

...

Let's start with a (contrived) example. Let's say you have an existing set of services that have methods that sometimes return null, and you want them to return an empty string instead because you are getting some NullPointerExceptions elsewhere in your application.

You could track down the implementation of each service and fix the logic that provides a return value ... or you could advise the methods:

No Formatcode
  @Match("*")
  public static void adviseNonNull(MethodAdviceRecieverMethodAdviceReceiver receiver)
  {
    MethodAdvice advice = new MethodAdvice()
    {
      void advise(Invocation invocation)
      {
        invocation.proceed();

        if (invocation.getResultType().equals(String.class) && invocation.getResult().equals( == null))
          invocation.overrideResult("");
      }
    };

    receiver.adviseAllMethods(advice);
  };

This is a method that is placed in a module class. Note the terminology: advise is the verb ("to advise a method") and advice is the noun ("with this advice"). The MethodAdviceReceiver is a wrapper around the service being advised: you can add advice to some or all methods of the service, and also obtain the interface of the service. It is automatically passed into service advisor methods.

The guide to IoC - injection describes what can be injected into a service advisor method.

Service advisor methods Anchormustmust must have a parameter of type MethodAdviceRecieverMethodAdviceReceiver.

A service will often be advised multiple times; any method may have any number of advice objects applied to it. Some methods may not get any advice. All of this is acceptibleacceptable.

Service advisor methods are always void methods (this is different than from service decorator methods).

The @Match @Match annotation indicates that this advice applies to all services (both your own, and those defined by Tapestry). You will want to narrow down which services are actually targetted targeted in most cases.

Note that some services, especially those built-in to Tapestry IoC, are marked as not subject to decoration, this applies to service advice as well as service decoration.

The MethodAdvice interface is very simple; it receives an Invocation representing a method call. Invocation has methods for inspecting the type and value of the parameters, and for overriding the values of the parameters.

The call to proceed() allows the invocation to continue; that is, the original method is invoked. If the method has been advised multiple times, the call to proceed() may chain into the next MethodAdvice object. In any case, after invoking proceed(), you may inspect and override the result (the return value).

Advice is pretty efficient, but it would still be better to apply it only to methods that make sense. We can improve our service advisor method to only advise methods that return String:

No Formatcode
  @Match("*")
  public static void adviseNonNull(MethodAdviceRecieverMethodAdviceReceiver receiver)
  {
    MethodAdvice advice = new MethodAdvice()
    {
      void advise(Invocation invocation)
      {
        invocation.proceed();

        if (invocation.getResult().equals(null))
          invocation.overrideResult("");
      }
    };

    for (Method m : receiver.getServiceInterface().getMethods())
    {
      if (m.getReturnType().equals(String.class))
        recieverreceiver.adviseMethod(m, advice);
    }
  };

Built-in Advice

Tapestry includes two built-in advisor services.

Logging Advice

Logging advice is built into Tapestry. You can apply logging advice to your services very easily:

No Formatcode
  @Match("*")
  public static void adviseLogging(LoggingAdvisor loggingAdvisor, Logger logger, MethodAdviceRecieverMethodAdviceReceiver recieverreceiver)
  {
    loggingAdvisor.addLoggingAdvice(logger, recieverreceiver);
  }

LoggingAdvisor is a built-in Tapestry IoC service. This demonstrates how services can be injected into service advisor methods. The Logger parameter is the logger for the service being advised.

...

LazyAdvisor makes method invocations lazy: methods that return an interface (rather than a value) will not execute immediately; instead, the method invocation is postponed until a method of the return value is invoked.

Matching And

...

Ordering

Each service advice method gets a unique id, obtained by stripping the "advise" prefix from the method name. Advice ids must be unique across all modules.

...

In many cases, the order in which the advice is given is very important; for example, you may want logging first, then transaction management, then security checks. The @Order @Order annotation allows you to explictly explicitly set the order.

Decorators and Advice

Service decorators are another way to achieve the same thing; service advisors are a more recent addition, added in Tapestry 5.1.

It is not recommended that you mix advice and decoration. If you do, decoration take precendenceprecedence; all decorators will be in effect before any advice (internally, they are two seperate separate steps, with advice being processed and the result of that used by the decorators).