Kontakt

Archive for September, 2010

Progress with Tapestry 5 book

Donnerstag, September 23rd, 2010

This week I had the pleasure to work on Tapestry 5 in Action book full time. I made some solid progress; approximately 20% finished. I expect the early access program to start soon, as the minimal amount of pages is already available. I also had a chance to choose the book cover from 3 alternatives. Stay tuned.

Validation Macros

Mittwoch, September 8th, 2010

Upgrading my current applications to Tapestry 5.2 is such a pleasure. I can throw away pieces of code that annoyed me in the past. Let me give you an example. Did you ever wish a simple-minded way of combining several related validator constraints together under a single name? If so, you will love the new feature called validator macros as I do.

Imagine you implemented a page which is used by your end users to change their passwords. The page contains a form with two password fields. It is obvious that both fields need to be validated in exactly the same way, thus you need to define your validation constraints twice as shown in the following listing (ChangePassword.tml).

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
   <form t:type="form">
      <t:errors/>
      <p>
         New Password:
         <input t:type="PasswordField" t:id="password"
                validate="required,minlength=10,maxlength=50"/>
      </p>
      <p>
         Repeat Password:
         <input t:type="PasswordField" t:id="passwordRepeated"
                validate="required,minlength=10,maxlength=50"/>
      </p> 

      <p>
         <input type="submit" value="Change It"/>
      </p>
    </form>
</html>

The page class is shown in the following listing:

public class ChangePassword {

   @Property
   private String password;

   @Property
   private String passwordRepeated;

   void onValidateForm() { ... }

   void onSuccess() { ... }
}

Needles to say, repeating some code is very ugly and should be avoided. As of¬†Tapestry 5.2 you can define new validators by combining several related validators together under a single name. The following listing shows how to accomplish this. You just contribute to the configuration of the ValidatorMacro service that is involved into the validation mechanism. The service’s configuration maps a validation macro name to a comma-separated list of validation constraints. When validating form fields Tapestry will ask this service to resolve a macro name (a simple string) to a set of validation constraint.

public class AppModule {

   @Contribute(ValidatorMacro.class)
   public static void combineValidators(
      MappedConfiguration<String, String> configuration) {
      configuration.add(
            "password",
            "required,minlength=10,maxlength=50");
   }
}

Once your made the contribution, it is possible to pass the password validation macro to the validate parameter of the form fields. This way you can avoid the redundancy of validation constraints for both password fields. See the following listing:

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
   <form t:type="form">
      <t:errors/>
      <p>
         New Password:
         <input t:type="PasswordField" t:id="password"
                validate="password"/>
      </p>
      <p>
         Repeat Password:
         <input t:type="PasswordField" t:id="passwordRepeated"
                validate="password"/>
      </p> 

      <p>
         <input type="submit" value="Change It"/>
      </p>
    </form>
</html>

Cool, isn’t? Tapestry 5.2 has a lot of improvements and I can’t await to see the final release.

New URL Rewriting API

Montag, September 6th, 2010

Since 5.1.0.1, Tapestry has basic support for URL rewriting based on the URLRewriter service. In 5.2.0 this service has been deprecated in favour of LinkTransformer service. The new service is actually a facade around ComponentEventLinkTransformer and PageRenderLinkTransformer services.

The PageRenderLinkTransformer service is responsible for the transformation of the page render requests. The implementation of the service is a chain of PageRenderLinkTransformer.

The ComponentEventLinkTransformer service allows a selective replacement of the default links used to represent a component event request. The implementation of the service is a chain of ComponentEventLinkTransformer. Now let’s see some examples.

Rewriting incoming URLs

Imagine you decided to migrate your existing application to Tapestry. Let’s say you are moving from JavaServer Faces. Because the navigation mechanisms in Tapestry and JSF are different, the URLs of your application will change in a incompatible way. This might annoy some of your users who bookmarked the URLs. After the migration these URLs will not work any more. In order to avoid it, you might rewrite all the incoming URLs that start with /jsf.

When processing an incoming requests Tapestry tries to determine if the request is a page render request. If so, Tapestry creates an instance of PageRenderRequestParameters which represents a page and its activation context. The created instance of PageRenderRequestParameters is then used by the PageRenderRequestHandler service to handle a page render request by activating and then rendering the page.

You can take over the task of creation of PageRenderRequestParameters by contributing a PageRenderLinkTransformer. The following listing shows how to accomplish this. When Tapestry attempts to decode the page render request, it will invoke the decodePageRenderRequest() method of JsfLinkTransformer. Inside this method we check if the request path is for a JSF page. If so, an instance of PageRenderRequestParameters representing the Index page with an empty activation context is returned. This will cause the rendering of the Index page when the /jsf/home.xhtml path is requested.

If the incoming request was not identified as a JSF request, a null value is returned. In this case the default logic for decoding the page render request will be executed.

public class JsfLinkTransformer implements PageRenderLinkTransformer {

   public PageRenderRequestParameters decodePageRenderRequest(
                  Request request) {
      String path = request.getPath();

      if (path.startsWith("/jsf/home.xhtml")) {
         return new PageRenderRequestParameters(
            Index.class.getSimpleName(),
            new EmptyEventContext(),
            false);
      }

      return null;
    }

   public Link transformPageRenderLink(
         Link defaultLink,
         PageRenderRequestParameters parameters) {
      return defaultLink;
   }
}

The class EmptyEventContext represents an empty activation context as shown in following example.

public class EmptyEventContext implements EventContext {

   public int getCount() {
      return 0;
   }

   public <T> T get(Class<T> desiredType, int index) {
      return null;
   }

   public String[] toStrings() {
      return new String[0];
   }
}

The next listing shows how to contribute an JsfLinkTransformer to the configuration of the PageRenderLinkTransformer service.

public class AppModule {

   @Contribute(PageRenderLinkTransformer.class)
   @Primary
   public static void provideURLRewriting(
      OrderedConfiguration<PageRenderLinkTransformer>
 configuration) {

      configuration.addInstance(
         "Faces", JsfLinkTransformer.class);
   }
}

Rewriting URLs within an Application

Imagine you want to deprecate a page inside your application in favour of a new page. Because some of your users might have bookmarked the old page you decided to keep it for a while. The URL of the page should still be accessible but all the links inside the application need to be updated. You can either go through all pages of your application to update the page parameter of the PageLink component or you can make a global replacement by contributing to the configuration of the PageRenderLinkTransformer service.

After creating a Link that encapsulates a page render request, Tapestry passes that Link through the PageRenderLinkTransformer chain of command. By contributing your own PageRenderLinkTransformer you can replace a passed link by another one. The following listing shows how to replace a link to MyPage by a link to AnotherPage globally. Inside the transformPageRenderLink() method the base path of the passed link is examined to identify the target page. If the Link represents a URL to MyPage, then a Link to AnotherPage is created and returned. Returning the original Link means that no transformation is required.

public class DeprecatePageLinkTransformer implements PageRenderLinkTransformer {

   @Inject
   private PageRenderLinkSource pageRenderLinkSource;

   public PageRenderRequestParameters decodePageRenderRequest(
                  Request request) {
      return null;
   }

   public Link transformPageRenderLink(
         Link defaultLink,
         PageRenderRequestParameters parameters) {

      if (defaultLink.getBasePath().contains("/mypage")) {
         return this.pageRenderLinkSource
               .createPageRenderLink(AnotherPage.class);
      }

      return defaultLink;
   }
}

The contribution in the following listing will cause that all the PageLink linking to MyPage will render a page link to AnotherPage event though the page parameter is set to MyPage.

public class AppModule {

   @Contribute(PageRenderLinkTransformer.class)
   @Primary
   public static void provideURLRewriting(
      OrderedConfiguration<PageRenderLinkTransformer>
 configuration) {

      configuration.addInstance(
         "DeprecatePageLink",
         DeprecatePageLinkTransformer.class);
   }
}

Rewriting Component Event URLs

Finally, the replacement of the default links used to represent a component event request is accomplished by the ComponentEventLinkTransformer service. This transformer follows the same pattern as PageRenderLinkTransformer.

Reminder: Three Tapestry Talks in September

Mittwoch, September 1st, 2010

As already posted here I’ll be giving a Tapestry talk at Herbstcampus¬†in N√ľrnberg and two Tapestry talks at WebAppDays conference in D√ľsseldorf.

The talk at Herbstcampus is a 70 minute Tapestry introduction and takes place on September 14th.

The both talks at WebAppDays¬†are scheduled on September 27th. The first talk will be a JSF 2.0 vs. Tapestry 5 comparison. It is the same talk I was giving at Jazoon, but this time I’ll be speaking together with Andy Bosch, a man behind JSF Forum. The second talk is a Tapestry only presentation and starts directly after the comparison.

Want to learn Tapestry? Come to Herbstcampus and/or WebAppDays.

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