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

Info

Starting with

Wiki Markup
{scrollbar}

Tapestry IoC Decorators

...

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.

Decoration is the which is a simpler mechanism for accomplishing the same thing.Decoration is the name of a popular design pattern. Using decoration, an existing object's behavior can be extended without changing the implementation of the object.

...

Tapestry IoC uses a similar approach, where a one or more of interceptor objects, all implementing the service interface, are strung together. The service's proxy (responsible for just-in-time instantiation of the service implementation) is at one end of this pipeline, the core service implementation is at the other.

...

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);
  } 
}

The method decorateIndexer() is a service decorator method because it starts with the word "decorate". In this simple case, only the myapp.Indexer service will be decorated, even if there are other services in this module or others ... this is because of the name match ("decorateIndexer" and "buildIndexer"), but we'll shortly see how annotations can be used to target many services for decoration.

...

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
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());
  } 
}

But as we'll see next, its it's possible to have a single decorator method work on many different services by using annotations.

Targeting Multiple Services

By using the @Match annnotation annotation, you may identify which services are to be decorated.

...

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);
  }   

...

Note: the ordering of decorators is in terms of the effect desired. Internally, the decorators are invoked last to first (since each once receives the "next" interceptor as its delegate). So the core service implementation is created (via a service builder method) and that is passed to the last decorator method. The interceptor created there is passed to the the next-to-last decorator method, and so forth.It should now be evident that the delegate passed into a decorator method is sometimes the core service implementation, and some times an interceptor object created by some other decorator method.

It should now be evident that the delegate passed into a decorator method is sometimes the core service implementation, and some times an interceptor object created by some other decorator method.

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);
  }

The decorator above is applied to any service whose id matches the "*DAO" pattern.

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);
  }

The decorator above is applied to any service that is marked by the @Blue annotation.

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);
  }

The decorator above is applied to any implementation of the MyService interface that is marked by the @Blue annotation.

Creating your own Decorators

...

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