Kontakt

Archive for Januar, 2010

New projects around Tapestry

Donnerstag, Januar 28th, 2010

In the last weeks some cool projects were announced in the Tapestry community.  Especially the developers of Wookie and Tynamo have been very busy.

Christophe Cordernier, Robin Komiwes and Bruno Verachten announced Wookie – a smart tool for collaborative writing. Wookie is an open source project built with Tapestry. It is a very nice demonstration of what you can achieve with Tapestry. Check out the demo. It is very cool.

The Tynamo Team announced 3 new projects:

  1. Tapestry Conversations: Conversations (known from JBoss Seam) for Tapestry
  2. Tapestry RESTEasy:  Modul for building RESTful Java applications based on RESTEasy.
  3. Tapestry JPA:  Full JPA 2 integration for Tapestry.

Tapestry IoC: Modularization of web applications without OSGi

Dienstag, Januar 19th, 2010

In this article I’m going to describe the modularization features of Tapestry. You will learn how to create modular applications that can be customized without to change the code of the application. These customizable applications can be delivered in different editions to different customers with minimal effort. I intend to publish a series of articles on this topic. This post is the first article of the series and starts with a short introduction to OSGi and its modularity features.

Modularization with OSGi

In the last years there has been a hype around OSGi. Most of the attention that OSGi has been getting is related to its modularization features.  With OSGi you can cut your application in several bundles which can be installed, updated or removed at runtime. No shutdown of the whole system is needed. Furthermore the OSGi Service Platform allows you to specify the dependencies between bundles on the package level. A bundle can hide the private API by exporting only those packages that are meant to be visible to other bundles. Hiding internal packages allows bundle developers to change the implementation details without to fear any backward compatibility issues. One of the killer features of OSGi is that you can version not only your bundle but also the packages exported by your bundles (actually exporting packages without a version is a bad practice). This way you never run into the incompatibility problem between libraries in your application. In a traditional JEE container you have a problem if your application depends on two libraries that depend on two incompatible versions of a third library. In contrast the OSGi dependency resolution mechanism allows you to deploy several versions of the same bundle.

OSGi is a great technology but there is an impact of using it. For example you will find yourself at a lower level of productivity because you spend too much time debugging the class loader problems of your bundles. If you start developing OSGi-based applications, then ClassNotFoundException will be your best friend.

Another issue is the fact that the majority of java libraries out there are not currently shipped as OSGi bundles. Fortunately SpringSource launched the Enterprise Bundle Repository where you can find OSGi-ready versions of open source libraries but this repository is not up-to-date. Often you will need to transform the needed library into a bundle. There are also too much libraries that advertise to be OSGi-ready because their developers put a MANIFEST.MF file into the JAR. The most OSGi-based developers will concede that too much bundles are just bad. Some examples are the usage of  Require-Bundle manifest header or export of unversioned packages. Also unversioned imports or imports with fixed version make your life harder then it should be. Imagine you found a cool bundle and want to install it. Then you find out that the bundle has some dependencies. At first you think it is no problem: there is another bundle that exports the required packages. But then you realize that the new bundle imports the version 1.2.3 but 1.2.2 is exported. Damn, a version range would save your time.

Next annoying issue is that some cool libraries, that work in a JEE environment out of the box, are incompatible per se with OSGi because they load classes with Class.forName. Every OSGi-based developer, who had the pleasure to integrate Hibernate into OSGi, knows what I’m talking about.

Web applications in OSGi are problematic

If you ever built OSGi-based web application you surely came across HttpService. This service is described in the OSGi Compendium Specification and is intended to register Servlets and resources in the OSGi environment. The most annoying thing about HttpService is that it supports only the Servlet 2.1 specification. Filters and listeners are not available. This limitation is quite inconvenient because almost every Java web framework uses filters and listeners.

There exists several solutions to deal with the limitations of the HttpService. One of them is Pax Web. This project extends the HttpService with support of recent servlet APIs. Another solution is Spring DM that allows you to deploy a WAR file into an OSGi container. I’m not going to describe these two projects in this post. Instead I would like to describe how you can accomplish modularization of web applications without OSGi.

Do you really need OSGi?

Before you decide to build OSGi-based web applications you have to ask yourself the question: Do you have a requirement to add services to your applications at runtime without restarting the JVM? I guess the most web applications don’t have this requirement. Another question is: Do you want to build modular applications? In most cases the answer to this question is yes. You might have found a reason for using OSGi but wait. If your aim is to break your monolithic WAR files into smaller, more easily managed modules you don’t need OSGi at all. There exist much simple and probably more elegant solutions to accomplish it. Tapestry IoC is one of the them. As far as I know Tapestry is the only one Java web framework that provides this functionality out of the box.

Tapestry IoC vs. OSGi

Tapestry IoC and OSGi have a lot in common. Similar to OSGi bundles Tapestry IoC Modules are packaged into JAR files. The MANIFEST.MF file acts as deployment descriptor for OSGi bundles and for Tapestry IoC Modules. When setting up the IoC registry, Tapestry automatically locates modules packaged into JARs. The framework scans through all JARs in the classpath and searches for a manifest entry Tapestry-Module-Classes inside MANIFEST.MF of every single JAR. The value of the entry is a comma-separated list of fully qualified class names of module classes. This means that a single JAR can contain multiple modules.

Manifest-Version: 1.0
Tapestry-Module-Classes: org.example.lib.LibraryModule, org.example.internal.AnotherModule

The OSGi bundles are identified in the same way. Every OSGi bundle has a MANIFEST.MF containing Bundle Manifest Headers. The headers BundleSymbolicName and Bundle-Version are required. The combination of these two headers is the unique identifier of the bundle inside OSGi. A BundleActivator is used to perform the bundle-specific activities necessary to start or stop a bundle. This is the place where services are registered or unregistered. In Tapestry services are registered in IoC Modules.

Both OSGi and Tapestry IoC provide support for service-oriented architectures. OSGi Service Platform provides a lightweight publish, find, and bind service model for services inside the JVM with the OSGi Framework service registry. Tapestry IoC also provides a Registry which is used to access the available services. While in OSGi you need to perform a service lookup, Tapestry provides a powerful Inversion of Control container. This container allows you to assemble your application from many small, easily testable pieces. Using Tapestry IoC you don’t need to lookup the dependencies of your services. Tapestry will instantiate your classes and resolve all the dependencies of your services. More details about Tapestry IoC can be found here.

Inverting the purpose of Component Libraries

Tapestry is a component-oriented web framework and allows you to create components with ease. The components are reusable by their nature and can be packaged into component libraries. What is exciting about Tapestry is that component libraries can also be used to create modular web applications. For this purpose you can invert the purpose of a component library: Instead of creating reusable libraries that are used in several applications you can create small libraries that are used only in a single application of your customer.

For example if you deliver the same application to several customers, you often have a problem that a special requirement of a customer conflicts with a special requirement of another customer. If you include these special wishes into the application, it will become bloated sooner or later. What you need is a feature that allows you to extend your application without to change it. In Tapestry this can be accomplished by component libraries. If one of your customer wishes a special page in the delivered application, you can create a small library that contains only this special page. This library is delivered to the customer as an additional JAR fie.  You don’t have to integrate this special page into your application.

A component library consists not only of components but also pages, mixins and services. This means that you can break your monolithic WAR file into several JARs, each containing a subset of pages. This way you can assemble your application from different pieces and deliver it to different customers. Your WAR file doesn’t have to contain any pages. When you deliver your application to a customer, you just include several libraries (JARs) depending on the needs of the customer.

Another use case is an application that is available in different editions. Just imagine that the Community Edition contains pages that are located inside the WAR file. The Professional Edition contains some further modules with additional pages and services. The upgrade from the Community Edition to the Professional Edition is just the matter of dropping a new JAR file onto the classpath and restarting the application. Nice, isn’t?

Creating component libraries is very simple. Pleas read this guide for more details.

Modularization example

Now let’s have a look at the modularization features of Tapestry. Imagine you need to create a Menu component which creates a navigation menu dynamically. Depending on the edition of the installed application Menu component renders a different set of menu items. For example when your customer upgrades to the Professional edition, the menu component doesn’t have to be changed. It just renders more items provided by the additional JAR.

Creating dynamic menus

The template of the Menu component might look like the following one. The Loop component iterates over a collection of page names and creates a link to the page of current iteration.

<div class="menu" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
   <ul>
      <li t:type="loop" source="pageNames"
          value="var:currentPage">
         <a t:type="pagelink" page="${var:currentPage}">
            ${var:currentPage}
         </a>
      </li>
   </ul>
</div>

Next we create the Java class of the component. The component retrieves the names of the available pages from the service ComponentClassResolver. This service is responsible for resolving page names and component types to fully qualified class names and can be injected into a page or component via the @Inject annotation. Just as with Tapestry applications, Tapestry component libraries have a unique base package. That’s why the service ComponentClassResolver is aware of all the pages available in all the modules of a Tapestry application.

@IncludeStylesheet("menu.css")
public class Menu {
   @Inject
   private ComponentClassResolver componentClassResolver;

   public List<String> getPageNames() {
      return this.componentClassResolver.getPageNames();
   }
}

Now create a simple Index page containing the Menu component.

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
   <body>
      <t:menu/>
   </body>
</html>

Start the server and call the URL of your application. The result should look like in the following picture.

If you would add a new page to your application, then the Menu component would show a new entry in the navigation menu. The same applies for component libraries. If you just drop a new JAR onto the classpath and restart your application, Tapestry will find all the pages, components, mixins and services inside the JAR. There is no need to change the code of the application if you want to add a new entry into the Menu. For example if you include the Tapestry/Hibernate integration library into you application the built-in page Statistics will be automatically added into the menu. The result looks like:

Because Tapestry contains some built-in pages the code above has to be changed slightly. You probably want to exclude the built-in pages from your menu. For this purpose we need an annotation to mark the pages to be included in the menu.

Defining the Annotation

We will place the following annotation in page classes to mark a page as an element of the menu.

@Target( { ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PluginPage {
}

For example we want the Index page to be included into the menu.

@PluginPage
public class Index {
   ...
}

Excluding Pages

Now let’s have a look at the modified Menu component. We iterate over the names of the available pages and convert every page name into a fully qualified class name. The class name is used to check if the @PluginPage annotation is present. If the annotation is not present, we filter out the page name.

@IncludeStylesheet("menu.css")
public class Menu {
   @Inject
   private ComponentClassResolver componentClassResolver;

   public List<String> getPageNames() {
      List<String> result = new ArrayList<String>();

      List<String> pages =
                   this.componentClassResolver.getPageNames();

      for (final String pageName : pages) {
         String className = this.componentClassResolver
                          .resolvePageNameToClassName(pageName);

         Class clazz = loadClass(className);

         if (clazz.isAnnotationPresent(PluginPage.class)) {
            result.add(pageName);
         }
      }

      return result;
   }

   private Class loadClass(final String className) {
      try {
         return Thread.currentThread()
                    .getContextClassLoader().loadClass(className);
      } catch (final ClassNotFoundException e) {
         throw new RuntimeException(e);
      }
   }
}

The result looks like:

Creating customer libraries

Now let’s assume one of your customer wishes a special page to be included into the application you delivered. Because we don’t want to change the application at all we create a simple component library that contains the new page. The structure of the library should look like in the following picture:

The class LibraryModule is the IoC Modul that is used to configure the application. Inside this module we tell Tapestry where to search for our page and component classes. This is accomplished by making a contribution to the ComponentClassResolver configuration. As you remember, we used this service in the Menu component to find the available pages. In the following example we map the prefix mylib to the root package of our library, namely org.example.lib.

public class LibraryModule {
    public static void contributeComponentClassResolver(
             Configuration<LibraryMapping> configuration){
        configuration.add(new LibraryMapping(
                    "mylib", "org.example.lib"));
    }
}

As you remember, Tapestry scans the classpath to find all IoC Modules. That’s why we need to put the following manifest entry into the MANIFEST.MF file.

Manifest-Version: 1.0
Tapestry-Module-Classes: org.example.lib.LibraryModule

Now build the JAR file, drop it onto the classpath and restart the application. You will see the the following result.

Conclusion

In this article you learned how to create modular applications without OSGi. If you don’t have the requirement to add services to your applications at runtime without restarting the JVM then the described solution is sufficient for your application. You don’t need to migrate to OSGi. Of course OSGi solves some other problems that can’t be handled inside an IoC Container. An example is the version incompatibility of included libraries: OSGi is the only one technology that allows you to install the same library in different versions. At the end of the day you have to decide which technology is more suitable for your needs. Tapestry IoC is a simple, lightweight (and maybe limited) solution for modularization of web applications.

In the next article I’m going to describe some more Tapestry features that allow you to create modular web applications.  Stay tuned.

Guest Lecture about Tapestry at University of Dortmund

Freitag, Januar 8th, 2010

University of DortmundJanuary 12th I’ll have the pleasure to guest lecture to Computer Science students at the University of Dortmund about Tapestry 5. This guest lecture takes place within the scope of the course name Web technologies that is held weekly by Prof. Dr. Dietmar Jannach.

I have a 90 minutes slot to show the students an alternative approach to create web applications. This guest lecture gives me an opportunity to demonstrate Tapestry to junior staff that is not biased by the standards of the industry. According to experience Computer Science students are eager to learn cool technologies. They judge the technologies by their easiness, power and elegance, not by buzz words.

What I’m going to show? First of all the fundamental concepts of Tapestry and how to get started. Then I’m going to show the difference to JSF. Furtunately Prof. Dr. Dietmar Jannach covered Servlets, JSPs and JSF in his lecture and I can expect some previous knowledge about Java web technologies. As always in my talks I’m going to show a lot of demos. Additionally I’ll be speaking about the management and development processes of open source projects at Apache Software Foundation.

Speaking at University of Dortmund is a honor for me because I studied Computer Science at this university. I remember the auditorium in which the lecture is taking place quite well. Some years ago I attended some lectures in this auditorium. This time I’ll be speaking and don’t have to pass any exams anymore.

Tapestry and JSR-303 Bean Validation API

Montag, Januar 4th, 2010

In the last days there has been a hype around JEE 6. One of the JSRs approved by the JCP is JSR-303 Bean Validation API. In this post I’m going to give you a preview of a nice Tapestry 5.2 feature, the JSR-303 integration library. I committed the code some weeks ago and you have to wait until Tapestry 5.2 is released if you want to use this library. You can also use the snapshots if you want to give it a try right now.

Tapestry Validation Mechanism

Tapestry provides a powerful validation mechanism which is described here. Among other things this mechanism allows you to annotate your domain model classes with the annotation @Validate. The annotation value is a comma separated list of validation constraints. Each constraint identifies a validator type and optionally a constraint value.

public class User {

    @Validate("required")
    private String username;

    @Validate("required,minlength=5")
    private String password;
}

The @Validate annotation is problematic if your domain model is used in non-Tapestry applications as well as in Tapestry applications. Your non-Tapestry application becomes dependent on Tapestry annotations.

JSR-303 Bean Validation API

The JSR 303 is a specification of the Java API for JavaBean validation in Java EE and Java SE. The JSR standardizes validation constraint declaration and provides a number of built-in constraints. Like in Tapestry the constraints are defined by constraint annotations. These annotations can be applied on properties of the JavaBeans.

public class User {

    @NotNull
    private String username;

    @NotNull @Size(min=5)
    private String password;
}

Tapestry and JSR-303 Bean Validation API

As of verison 5.2 Tapestry will provide an JSR-303 integration library that allows you to annotate your domain model with JSR-303 annotations.  The library is responsible for configuring and bootstrapping the javax.validation.Validator for you. In order to use this library you have to choose an implementation of the JSR-303 specification like Hibernate Validator 4.x. Once you have included an implementation of JSR-303 into your application the service BeanValidatorSource will create an instance of  javax.validation.ValidatorFactory which is used to create Validator instances.

The configuration of BeanValidatorSource is a list of BeanValidatorConfigurer. You can contribute a BeanValidatorConfigurer to the configuration of this service in order to participate on the configuration of javax.validation.Validator. Here is an example how to do it:

public class AppModule {

   public static void contributeBeanValidatorSource(
      OrderedConfiguration<BeanValidatorConfigurer> conf) {

      BeanValidatorConfigurer configurer
               = new BeanValidatorConfigurer() {
         public void configure(
             javax.validation.Configuration<?> aConf) {
            aConf.ignoreXmlConfiguration();
         }
      };
      conf.add("MyConfigurer", configurer);
   }
}

That’s it. If you are using the BeanEditForm component to create forms for your domain model objects you don’t have to change any of your pages. Just pass the object to be edited to the object parameter, BeanEditForm will detect the JSR 303 annotations and validate the user input accordingly.

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
   <body>
      <t:beaneditform object="user"/>
   </body>
</html>

If you create your forms manually rather than generating them with BeanEditForm you might be interested in how to make the Form component interpret the JSR 303 annotations. Let’s have a look at a simple Login page. The properties userName and password of this page are bound to fields of the form. These properties are annotated with both Tapestry’s and JSR 303 annotation. Now to make Form component analyze the JSR 303 annotations you have to pass the object to be validated into the Forms’ parameter validate. In this examaple we pass the page instance itself.

public class Login {
   @NotNull
   @Property @Persist
   private String userName;

   @NotNull
   @Validate("minlength=10")
   @Property @Persist
   private String password;

   void onSuccess() {
      // Login the user here
   }
}
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
   <body>
      <t:form validate="this">
         <t:errors/>

         <t:textfield t:id="userName"/>

         <t:textfield t:id="password"/>

         <input type="submit" value="Login"/>
      <t:form>
   </body>
</html>

Alternatively you can process the validation manually. For this purpose just remove validate=”this” from the template and inject the Validator into you page. Let’s see how it works.

Injecting Validator

The created javax.validation.ValidatorFactory is registered in the Tapestry Registry and can be injected into you pages, components or services to create Validator instances. You can also let Tapestry create a Validator object for you which can be injected into your page, component or service. In the following example we inject a Validator instance into the Login page and validate the use input in the onValidateForm() handler method. The violations of the constraints are then recorded in the form and presented to the user.

public class LoginPage {
   @NotNull
   @Property @Persist
   private String userName;

   @NotNull
   @Validate("minlength=10")
   @Property @Persist
   private String password;

   @Inject
   private Validator validator;

   @InjectComponent
   private Form form;

   void onValidateForm() {
      Set<ConstraintViolation<LoginPage>> errors
           = validator.validate(this, Default.class);

      for (ConstraintViolation<LoginPage> next : errors) {
         form.recordError(next.getMessage());
      }
   }

   void onSuccess() {
      // Login the user here
   }
}

Limitations

Unfortunately the JSR 303 doesn’t cover the client-side validation, so that most web frameworks supporting this JSR will come up with proprietary solutions. Tapestry’s integration library doesn’t provide any client-side validators for JSR 303 annotations so far. I’ll announce this missing feature in a separate post. Stay tuned.

Tapestry 5.0.19 released

Sonntag, Januar 3rd, 2010

I’m pleased to announce the Tapestry 5.0.19 Bug Fix release which contains a fix for TAP5-815, the second most popular Tapestry issue. If you are still a Tapestry 5.0.x user you should upgrade to 5.0.19 because we fixed a serious security bug in this release. The release also contains some performance improvements. Check out the release notes for further details.

The binaries are available for download here. In few days we are going to release Tapestry 5.1.0.7.

Release Notes – Tapestry 5 – Version 5.0.19

Bug

  • [TAP5-815] – Asset dispatcher allows any file inside the webapp visible and downloadable
  • [TAP5-419] – Logging of component class transformations has typo in prefix
  • [TAP5-449] – @CleanupRender Render phase methods not always invoked
  • [TAP5-894] – Fix PartialMarkupDocumentLinkerTest.stylesheet_link()
  • [TAP5-936] – Tapestry wiki link links to nothing for locales other then en

Improvement

  • [TAP5-417] – Tapestry Performance Improvements
  • [TAP5-762] – Upgrade Selenium dependencies to version 1.0.1
  • [TAP5-934] – Update javassist version + improve error reporting in OneShotLock

Task

  • [TAP5-819] – remove ide-specific files from all sub-modules and add them to svn:ignore

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