Kontakt

Archive for Juli, 2010

Reading Annotations of Service Implementation Classes

Mittwoch, Juli 28th, 2010

Today I committed some changes for Tapestry 5.2 that allow you to access annotations placed on service implementation classes. As you probably know Tapestry creates proxies for services. The proxy implements the service interface and delegates method invocations to the actual service instance. When you inject a service into your page, component or another service you get a proxy which creates an instance of the service implementation class on demand. The first time a method is invoked on the proxy, the actual service is instantiated and initialized.

Until today Tapestry’s proxies did not expose annotations of service implementation classes. If you have placed annotations on a service implementation class you was not able to read them reflectively. The reason was the way how proxy classes were fabricated. Tapestry just skipped copying annotations from implementation class to proxy. This caused some problems when integrating third party libraries into Tapestry applications. For example imagine you want to expose your service as a web service and place the @WebService on the service implementation class. Because the annotation was not copied to the service proxy class, your service was not identified as web service.

import javax.jws.WebService;
import javax.jws.WebMethod;

@WebService
public class CalculatorImpl implements Calculator {  

   @WebMethod
   public int add( int x, int y )  {
      return x + y;
   }
}

As of version 5.2 the annotations from service implementation class will be placed on the proxy class if this implementation is bound to its interface inside a bind method.

public class AppModule {  

   public static void bind(ServiceBinder binder)  {
      binder.bind(Calculator.class, CalculatorImpl.class);
   }
}

In case of build methods annotations are still lost. For build methods there is no way to determine the implementation class at proxy creation time. A proxy invokes the build method to instantiate the actual service on demand. At this time you know the implementation class but it’s just too late. The proxy class already have been fabricated.

public class AppModule {  

   public static Calculator buildCalculator()  {
      return new CalculatorImpl();
   }
}

Tapestry Talk at Herbstcampus

Montag, Juli 26th, 2010

I was invited to present Tapestry 5 at Herbstcampus in N√ľrnberg, Germany. Herbstcampus is a conference for software developers, architects, and project managers with a technology focus in .NET and Java. My talk Fliegede Teppiche – Moderne Webanwendungen mit Tapestry 5 (Flying carpets – Modern web applications with Tapestry 5) takes place on September 14th, 2010. If you are living in Bavaria, this is a chance to learn Tapestry in a 70 minutes session. I‚Äôll give an overview over Tapestry concepts and new features in the upcoming release¬†5.2. As always I will show a lot of demos.

Advanced Service Builders in Tapestry IoC: Chain of Responsibility Pattern

Dienstag, Juli 20th, 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.

  • HttpBasicAuthenticationService: authentication based on HTTP Basic authentication header
  • HttpDigestAuthenticationService: authentication based on HTTP Digest authentication header

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.

public class AppModule {

   public static void bind(ServiceBinder binder) {

      binder.bind(AuthenticationService.class,
                  HttpBasicAuthenticationService.class)
            .withId("HttpBasic");
      binder.bind(AuthenticationService.class,
                  HttpDigestAuthenticationService.class)
            .withId("HttpDigest");
   }
}

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.

public class AuthenticationFilter implements ComponentRequestFilter {

   @InjectService("HttpBasic")
   private AuthenticationService basicAuthService;

   @InjectService("HttpDigest")
   private AuthenticationService digestAuthService;

   @Inject
   private Response response;

   public void handleComponentEvent(
         ComponentEventRequestParameters parameters,
         ComponentRequestHandler handler) throws IOException {

      if (isAuthenticated()) {
         handler.handleComponentEvent(parameters);
      }
   }

   public void handlePageRender(
         PageRenderRequestParameters parameters,
         ComponentRequestHandler handler) throws IOException {

      if (isAuthenticated()) {
         handler.handlePageRender(parameters);
      }

   }

   private boolean isAuthenticated() throws IOException {

      if (this.basicAuthService.isAuthenticated()) {
         return true;
      }

      if (this.digestAuthService.isAuthenticated()) {
         return true;
      }

      this.response.setHeader("WWW-Authenticate",
            "Basic realm=\"Tapestry\"");

      this.response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
            "Request unauthorized");

      return false;
   }
}

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.

public class AppModule {

   public static void contributeAuthenticationService(
       OrderedConfiguration<AuthenticationService> config) {

      config.addInstance("HttpBasic",
              HttpBasicAuthenticationService.class);
      config.addInstance("HttpDigest",
              HttpDigestAuthenticationService.class);
   }

   public static AuthenticationService buildAuthenticationService(
         List<AuthenticationService> configuration,
         ChainBuilder chainBuilder) {

      return chainBuilder.build(AuthenticationService.class,
                                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.

public class AuthenticationFilter implements ComponentRequestFilter {

   @Inject
   private AuthenticationService authenticationService;

   @Inject
   private Response response;

   public void handleComponentEvent(
         ComponentEventRequestParameters parameters,
         ComponentRequestHandler handler) throws IOException {

      if (isAuthenticated()) {
         handler.handleComponentEvent(parameters);
      }
   }

   public void handlePageRender(
         PageRenderRequestParameters parameters,
         ComponentRequestHandler handler) throws IOException {

      if (isAuthenticated()) {
         handler.handlePageRender(parameters);
      }

   }

   private boolean isAuthenticated() throws IOException {

      if (this.authenticationService.isAuthenticated()) {
         return true;
      }

      this.response.setHeader("WWW-Authenticate",
            "Basic realm=\"Tapestry\"");

      this.response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
            "Request unauthorized");

      return false;
   }
}

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.

public class HttpBasicAuthenticationService implements AuthenticationService {
   @Inject
   private Request request;

   public boolean isAuthenticated() {
      String header = this.request.getHeader("Authorization");

      if (header == null || !header.startsWith("Basic ")) {
         return false;
      }

      String[] tokens = getCredentials(header);

      if (tokens.length != 2) {
         return false;
      }

      return tokens[0].equals("igor")
                  && tokens[1].equals("secret");

   }

   public String[] getCredentials(String header) {
      ...
   }
}

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.

Here is see an extract from TapestryModule:

public class TapestryModule {

    public void contributeComponentClassTransformWorker(
        OrderedConfiguration<ComponentClassTransformWorker>
                     configuration){

        configuration.addInstance("Inject",
                                  InjectWorker.class);
        configuration.addInstance("InjectService",
                                  InjectServiceWorker.class);
        configuration.addInstance("InjectPage",
                                  InjectPageWorker.class);

        ...

        configuration.add("OnEvent", new OnEventWorker());

        ...
        configuration.addInstance("Parameter",
                                  ParameterWorker.class,
                                  "after:Inject*");
   }

   public ComponentClassTransformWorker buildComponentClassTransformWorker(
         List<ComponentClassTransformWorker> configuration,
         ChainBuilder chainBuilder) {

      return chainBuilder.build(
                           ComponentClassTransformWorker.class,
                           configuration);
   }
}

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.

Tapestry 5 Blog - Copyright © 2009 - Eclectic Theme by Your Inspiration Web - Powered by WordPress