Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin

 

Scrollbar

Wiki Markup
{scrollbar}

...

Info

Starting with Tapestry 5.1, Service Decoration is augmented with Service Advice. Advisors are similar but more general, as they work on any service interface, which doesn't have to be known at build time. Decoration is used when the type of the service being decorated is known at build time, and involves supplying a new implementation of the service interface.

...

Service Decoration Methods

Code Block
languagejava
titleAppModule.java (partial)

package org.example.myapp.services;

import org.apache.tapestry5.ioc.services.LoggingDecorator;
import org.slf4j.Logger;

public class MyAppModuleAppModule
{
  public static Indexer build()
  {
    return new IndexerImpl();
  }

  public static <T> T decorateIndexer(Class<T> serviceInterface, T delegate,
    String serviceId, Logger logger,

    LoggingDecorator decorator)
  {
    return decorator.build(serviceInterface, delegate, serviceId, logger);
  }
}

...

Alternately, when targetting services whose type is known at compile time, you may provide a parameter whose type matches the service interface. For example, decorateIndexer() will always be applied to the Indexer service, whose type (Indexer) is known. We could therefore rewrite decorateIndexer() as:

Code Block
java
languagejava

  public static Indexer decorateIndexer(Indexer delegate, Logger logger, LoggingDecorator decorator)
  {
    return decorator.build(Indexer.class, delegate, "Indexer", logger);
  }

Of course, nothing stops you from combining building with decorating inside the service builder method:

Code Block
languagejavajava
titleAppModule (partial)

package org.example.myapp.services;

import org.apache.tapestry5.ioc.services.LoggingDecorator;
import org.slf4j.Logger;

public class MyAppModuleAppModule
{
  public static Indexer build(Logger logger, LoggingDecorator decorator)
  {
    return decorator.build(Indexer.class, logger, new IndexerImpl());
  }
}

...

For example, to target all the services in your module:

Code Block
java
languagejava

  @Match("*")
  public static <T> T decorateLogging(Class<T> serviceInterface, T delegate,
    String serviceId, Logger logger,
    LoggingDecorator decorator)
  {
    return decorator.build(serviceInterface, delegate, serviceId, logger);
  }   

...

For example, you almost always want logging decorators to come first, so:

Code Block
java
java

  @Match("*")
  @Order("before:*")
  public static <T> T decorateLogging(Class<T> serviceInterface, T delegate,
    String serviceId, Logger logger,
    LoggingDecorator decorator)
  {
    return decorator.build(serviceInterface, delegate, serviceId, logger);
  }   

...

Annotation driven decorators

Since
since5.2
 
Starting from version 5.2, Tapestry supports annotation-driven decorator methods. If the @Decorate annotation is present, the decorator method can be arbitrary named, as shown in the following example.

Code Block

  @Decorate
  @Match("*DAO")
  public static <T> T byServiceId(Class<T> serviceInterface, T delegate,
    String serviceId, Logger logger,
    LoggingDecorator decorator)
  {
    return decorator.build(serviceInterface, delegate, serviceId, logger);
  }

...

Alternatively, marker annotations can be placed on the decorate method to match a specific service.

Code Block

  @Decorate
  @Blue
  public static <T> T byMarkerAnnotation(Class<T> serviceInterface, T delegate,
    String serviceId, Logger logger,
    LoggingDecorator decorator)
  {
    return decorator.build(serviceInterface, delegate, serviceId, logger);
  }

...

By default, @Decorate annotation applies the decorator to any service matched by the @Match or marker annotations. You can limit the matching to a single service interface, as shown in the following example.

Code Block

  @Decorate(serviceInterface=MyService.class)
  @Match("*DAO")
  public static <T> T byServiceId(Class<T> serviceInterface, T delegate,
    String serviceId, Logger logger,
    LoggingDecorator decorator)
  {
    return decorator.build(serviceInterface, delegate, serviceId, logger);
  }

In the example above, the decorator is applied to any implementation of MyService interfaces whose id matches the "*DAO" pattern.

Code Block

  @Decorate(serviceInterface=MyService.class)
  @Blue
  public static <T> T byMarkerAnnotation(Class<T> serviceInterface, T delegate,
    String serviceId, Logger logger,
    LoggingDecorator decorator)
  {
    return decorator.build(serviceInterface, delegate, serviceId, logger);
  }

...

By way of an example, we'll show an implementation of the LoggingDecorator service:

Code Block
languagejava
titleLoggingDecoratorImpl.java

public class LoggingDecoratorImpl implements LoggingDecorator
{
    private final AspectDecorator aspectDecorator;

    private final ExceptionTracker exceptionTracker;

    public LoggingDecoratorImpl(AspectDecorator aspectDecorator, ExceptionTracker exceptionTracker)
    {
        this.aspectDecorator = aspectDecorator;
        this.exceptionTracker = exceptionTracker;
    }

    public <T> T build(Class<T> serviceInterface, T delegate, String serviceId, final Logger logger)
    {
        final ServiceLogger serviceLogger = new ServiceLogger(logger, exceptionTracker);

        MethodAdvice advice = new MethodAdvice()
        {
            public void advise(Invocation invocation)
            {
                boolean debug = logger.isDebugEnabled();

                if (debug) serviceLogger.entry(invocation);

                try
                {
                    invocation.proceed();
                }
                catch (RuntimeException ex)
                {
                    if (debug) serviceLogger.fail(invocation, ex);

                    throw ex;
                }

                if (!debug) return;

                if (invocation.isFail())
                {
                    Exception thrown = invocation.getThrown(Exception.class);

                    serviceLogger.fail(invocation, thrown);

                    return;
                }

                serviceLogger.exit(invocation);
            }
        };

        return aspectDecorator.build(serviceInterface, delegate, advice,
                                      String.format("<Logging interceptor for %s(%s)>", serviceId,
                                                    serviceInterface.getName()));
    }
}

...

The AspectDecorator service can also be used in more complicated ways: it is possible to only advise some of the methods and not others, or use different advice for different methods. Check the JavaDoc for more details.

...

 

Scrollbar