Kontakt

Advanced Service Builders in Tapestry IoC: Chain of Responsibility Pattern

Posted on Dienstag, 20th Juli, 2010

In the article Advanced Service Builders in Tapestry IoC: Strategy Pattern I described how to implement the Strategy pattern with Tapestry. In this post I’ll cover the Gang of Four Chain of Responsibility Pattern. I’ll start with a simple example that demonstrates how to implement the pattern in Tapestry. At the end of the article, I’ll show you how the Chain of Responsibility Pattern is used inside Tapestry.

The use case

Suppose you need to support several authentication mechanisms in your application. Let’s say you need to support authentication based on both HTTP Basic authentication header (RFC 1945) and HTTP Digest authentication header (RFC 2617). Probably you would specify a service interface like AuthenticationService:

public interface AuthenticationService {
boolean isAuthenticated();
}

You would also provide two implementations of the service. The implementation details are not interesting yet.

Both implementations can be bound to their interface in the application module. Note that you need to provide the service id to disambiguate both implementations of the AuthenticationService interface.

Naive Approach

Now let’s first have a look at a naive usage of the AuthenticationService service. Let’s implement a simple ComponentRequestFilter which is responsible for recognizing whether an incoming request is authenticated or not.¬† In the following example¬†we need to inject both implementations of AuthenticationService service using the @InjectService annotation. The value of the annotation is used to identify the service to be injected. When a component event request is handled or a page is rendered we check if the request is authenticated by invoking both services HttpBasic and HttpDigest one after another. If the request is unauthenticated, the filter will set the WWW-Authenticate header and send a 401 HTTP error indicating the request requires HTTP authentication.

Injecting both services¬†HttpBasic and HttpDigest and invoking them one after another is not really nice.¬†If you decide to provide a further authentication mechanism in the future, you’ll have to change the AuthenticationFilter class. Imagine you decided to support the authentication based on a Remember-Me-Token. In this case you need to provide a new implementation of AuthenticationService interface¬†which will be injected into AuthenticationFilter using the service id. Now you have three different instances of AuthenticationService that are invoked one after another.

Chain of Responsibility

Now let me show you how to improve our example. We’ll implement the AuthenticationService as a Chain of Responsibility. Instead of providing different implementations of AuthenticationService interface with dfferent service ids, we use the ChainBuilder service to build a chain of AuthenticationService inside a build method.

The ChainBuilder service is used to assemble a Chain Of Command implementation of an interface (the command interface) from¬†an ordered list of objects implementing that interface (the commands). The commands of the chain are contributed to the service configuration.

The result is that there is a single instance of AuthenticationService service inside the Registry. You don’t need to disambiguate different instances of the service by providing unique ids and don’t have to care which instance to inject. You just inject the AuthenticationService service and use it. Note that we are not using the @InjectService annotation anymore. Now it is fine to use the¬†@Inject annotation. Tapestry will call the Chain of Command behind the scenes for you. Now AuthenticationFilter can be simplified.

Now that you know how to create a Chain of Command with Tapestry, let me explain you how Tapestry works down the list of commands.

ChainBuilder Service

As you already learned, the ChainBuilder service creates a Chain Of Command based on a command interface and commands. Behind the scenes Tapestry generates an implementation of the command interface at runtime. For each method in the interface, the chain implementation will call the corresponding method on each command object in turn.¬† There are several situations when a chain is terminated:

If the return type of the interface method is boolean and a command in the chain returned true.

If the return type of the interface method is numeric and a command in the chain returned a non-zero value.

If the return type of the interface method is other than boolean or numeric and a command in the chain returned a non-null value.

If a command in the chain throws an Exception.

Note that in case of void methods only throwing an Exception can terminate the chain.

Now let me explain how chains can be terminated. In the following example you can see HttpBasicAuthenticationService service. It first checks if the Authorization header is set. If the header is not available, then false is returned. In this case the next command in the chain is invoked. If the header is present the command executions continues. If the credentials are available and valid, then true is returned. This terminsates the chain.

Real Life Example

Now that you know how to implement the Chain Of Command Pattern let me show you how this pattern is used inside Tapestry. As you know Tapestry performs the transformation of the page and component classes at runtime. This bytecode transformation is performed by the ComponentClassTransformWorker service which is implemented as a Chain of Command. The commands of the chain transform a component or page class in turn. For example there is an internal implementation of the¬†¬†ComponentClassTransformWorker interface which is responsible for transforming¬†a component or a page¬†class¬†in that way that¬†you as page author can¬†inject¬†services into that component or page via @Inject annotation. Another command in the chain is responsible for identifying parameters via the @Parameter annotation on component fields. Almost for every annotation, used in a component or a page, there is a specialized command which is a part of the class transformation chain.

Summary

In this post I described how to implement Chain of Responsibility Pattern using Tapestry IoC. Based on the authentication example you learned how to decouple a sender of a request and its receiver by giving more than one service a chance to handle the request.

In the next post of this series I’ll cover further advance techniques for building services. You’ll learn how to use Shadow services.