Learning Tapestry, the Inversion of Control Framework

The framework

I recently started to use Apache Tapestry for work.

Tapestry is a web component framework with ambitious goals. E.g. it’s a web component framework that’s easy to extend. Also, it gives you the ease of development of scripting languages within the java ecosystem. Moreover, it provides an Inversion of Control (IoC) container with nice aspect-oriented features too.

This is my learning log: I completed the official tutorial. Then completed an in-house tutorial that shows how tapestry and the other moving parts fit together in our application. Finally, I read the Tapestry 5 book.

Earlier I wrote about its component and scripting parts. Here I’ll dissect its Dependency Injection parts

What is dependency injection?

J.B. Rainsberger wrote an excellent post about Dependency Injection in general. Here is a brief summary:

When an object doesn’t create its collaborators, but instead a caller provides them then, then it’s dependency injection.

So if an object receives its collaborators through the constructor, then it’s constructor-based injection. If it receives them through setter methods, then it’s property-based injection. If the collaborators are automagically set into the fields without constructor or setter then its field-based dependency injection.

Why dependency injection good for us?

It’s good for us because you don’t have to type ‘new’ that often. Pun intended.

Dependency injection has a couple of advantages. First of all, it doesn’t tie one implementation class to another. I.e. your code depends on abstractions like EmailSender while you can configure to use RealEmailSender for production and FakeEmailSender for testing.

Another advantage is that your framework can lazy-load your classes. So you can get the performance benefit without having to write extra code like Proxy pattern. And that’s a great win, even though there’s default class for that.

Funny, huh? When you talk about dependency injection, people will think about long xml configurations and annotation through the code. But that’s just a pattern that Spring and Java EE follows. Here we’ll see how Tapestry implements DI.

But then, what’s an IoC container?

As a one-liner: an IoC container is an actual implementation of the concept of Dependency Injection.

Other than that, an IoC container is a controversial thing. On the one hand, it makes it nicely separates configuration from the rest of your code. On the other hand, it makes you to deal with complicated framework like Spring or Java EE.

Tapestry services

A service in Tapestry is an object that you can inject into your pages. See, in Tapestry calls the dependencies services.

Tapestry has lots of services that help you to do stuff.

First of all, you can configure Tapestry in class called AppModule. Well, that’s not entirely true. Tapestry defines three environments: Production, QA and Test. You can configure then in AppModule, QaModule and TestModule respectively.

This might look a bit like magic. But it follows the usual Dependency Injection pattern. It’s common to see DI configuration in xml, like in Swing and Java EE. But Guice too lets you configure your classes in java.

For Tapestry-IoC, this is not the end of the story. The heart of Tapestry-IoC is a Registry. This is where the services are stored to and retrieved from. This Registry is configured through various modules. There are lots of predefined modules. AppModule QaModule, TestModule happen to be the ones that you can tweak.

A custom service

Let’s pretend there’s a custom service that we need to inject. Basically, it can be anything. I.e. it can help us to persist object into a mongoDB database. It can invoke a complicated calculation in a 3rd party system called AIMMS.

It can also be a FooBar service. A Foobar service has a foo() method that returns a Bar object. From a programming point of view, there’s an object that has some methods that we can invoke from our Tapestry pages.

So, this is our service definition:

public interface FooBar {
    public Bar foo();
}

This interface has a production-quality implementation: FooBarImpl:

public class FooBarImpl implements FooBar {
    public FooBarImpl() {
        // Default no-arg constructor.
    }

    @Override
    public Bar foo() {
        //... doing important stuff
    }
}

And this is how they’re wired together:

public class AppModule {
    public static void bind(ServiceBinder binder) {
        binder.bind(FooBar.class, FooBarImpl.class);
    }
}

Why is this so simple?

FooBarImpl has a no-arg constructor so Tapestry knows how to build the service. There are some sophisticated rules how Tapestry finds the right constructor and injects its parameters – but I’ll skip it for the sake of simplicity.

For the record, you can take more control over instantiation your service: you can write your builder method. In that method you can do whatever you need to initialize your service:

public class AppModule {
    public static FooBar buildFooBar() {
        ExperimentalFooBarImpl fooBar = new ExperimentalFooBarImpl("Haruki Murakami");
        return fooBar.dance().dance().dance();
    }
}

How do you use it?

Tapestry has field-based injection. So you just create a private field, annotate it with @Inject, that’s it:

public class WriterPage {
    //...
    @Inject
    private FooBar fooBar;
    //...
    @OnEvent
    public Object handle() {
        return fooBar.foo();
    }
}

Design issues

You could argue that injecting heavy-weight services into Tapestry pages isn’t right. You could come up with alternatives, like using another IoC container that injects Tapestry into other objects. However, your web pages are going to need an abstract service that they can call on user actions. At the end of the day, you’ll have to inject stuff into your pages.

Still, you could ask, how do we inject not-so-controversial objects into our pages? Well, actually, the @Component annotation injects a Tapestry component. It can inject default components like Select, as well as it can inject your custom components too.

So why didn’t I show examples like this? Because Tapestry provides a built-in method to inject them. Part of this built-in method is the ComponentClassResolver that will find the components if you place them into the right package. Another part is the TapestryModule which has lots of built-in logic. Remember, AppModule is a module that you can tweak. Well, TapestryModule is one that you can not. Still, it’s nice to recognize the IoC methods in its source code.

By the way, I covered components in the previous post.

Contributing Value Encoders

You can not only define new services. You can contribute smaller sub-services to them too. ValueEncoders are a good example for this.

ValueEncoders define how you can serialize and deserialize objects from and to String. They are used to send objects as HTTP parameters. Suppose you want to use a specific ValueEncoder. The only thing you need to do is to write a specific encoder, then contribute it to the AppModule:

publicclassAppModule {
    ...
    @Contribute(ValueEncoderSource.class)
    public staticvoidprovideEncoders(MappedConfiguration<class, <span="" class="hiddenSpellError" pre="" data-mce-bogus="1">ValueEncoderFactory> configuration,final BlogService blogService) {
        ValueEncoderFactory</pre>
<article> factory = new ValueEncoderFactory<article>() {
            public ValueEncoder<article> create(Class<article> clazz) {
                return ...;
            }
        }
        configuration.add(Article.class, factory);
    }
}

So what can we see here?

You can contribute your code in the very same class where you can define the services. All you need to do is to write a ValueEncoderFactory, then add it to the MappedConfiguration. ValueEncoderFactory is a Factory class so Tapestry can defer creating this encoder. MappedConfiguration works like a map so you can add your app-specific configurations.

Benefits of Proxy pattern

Earlier I mentioned the proxy pattern. Tapestry-IoC actually uses proxies for its services – given that the service has one interface and one or more implementations. Let’s take a quick look on some of those benefits:

Service Status

You can check the service status of your web app. All you need to do is to run your app in development mode and type http://yourhost/appname/servicestatus in your browser. See this page for further details.

On that page you’ll see a list of your services. Some of them are loaded while others were not even referenced. So you have lazy-loading out of the box. Compare it with the burden of writing and maintaining it yourself.

Writing lazy-loading is more difficult than you think. See this SO page if you have any doubts.

Method Advices

Proxy objects let you write interceptors. Also known as aspects. These things let you do things before and/or after a certain method runs. These things let you do it very conveniently: you can write this before/after code in one class and apply them to another.

The typical method advice examples are logging, security checks and transactions. No wonder that the CommitAfter annotation is implemented with a method advice.

The HibernateModule (see, another Tapestry IoC module) has this method:

    /**
     * Adds the CommitAfter annotation work, to process the
     * {@link org.apache.tapestry5.hibernate.annotations.CommitAfter} annotation.
     */
    @Contribute(ComponentClassTransformWorker2.class)
    @Primary
    public static void provideCommitAfterAnnotationSupport(OrderedConfiguration configuration)
    {
        // If logging is enabled, we want logging to be the first advice, wrapping around the commit advice.
        configuration.addInstance("CommitAfter", CommitAfterWorker.class, "after:Log");
    }

If you check what CommitAfterWorker does, it’ll lead you to the CommitAfterMethodAdvice. In the advise method we can see the real transaction handling logic:

@Override
public void advise(final MethodInvocation invocation)
{
    //... skipped some stuff for brevity
    if (transaction != null && !transaction.isActive())
    {
        transaction.begin();
    }
    
    try
    {
        invocation.proceed(); // going on with the method being annotated
    } catch (final RuntimeException e)
    {
        if (transaction != null && transaction.isActive())
        {
            rollbackTransaction(transaction);
        }
    
    throw e;
    }
    
    // Success or checked exception:
    
    if (transaction != null && transaction.isActive())
    {
        transaction.commit();
    }
    //... skipped some stuff for brevity
}

Wrapping up

This is what we learned so far:

  • Now we know what’s dependency injection and Inversion of Control
  • We know that Tapestry has an extensible Inversion of Control framework
  • We saw examples on how to extend this framework
  • We know that tapestry relies heavily on this framework
Advertisements

About tamasrev

A software developer, specialized in Java, addressing himself as generalist. A proud daddy.
This entry was posted in java, programming, web and tagged , , , . Bookmark the permalink.

4 Responses to Learning Tapestry, the Inversion of Control Framework

  1. Kofa says:

    ‘long xml configurations and annotation through the code. But that’s just a pattern that Spring and Java EE follow’
    Well, I’m sure you’re aware of the fact that there has been progress. With Spring, you can get rid of XML, and with the use of Spring Java Configuration, you can get rid of annotations in your business code (you’ll have them in @Configuration classes). Also, your Tapestry example uses @Inject and @Component, so I don’t quite understand your point making that sentence. That is not to say Tapestry is bad or anything – I’m not a web developer and I’ve read very little about Tapestry, and even that is outdated by now (was v3 and v4).

    • tamasrev says:

      Kofa, thanks for pointing out the Spring improvements. You just added something to my reading list. Also, a note that I need to improve my writing.

      That sentence mostly a reflection on my knowledge (ignorance). When somebody says Dependency Injection, my first association was lots of xml and/or lots of annotations.

      I literally believed that it was right, until I read this post from J.B. Rainsberger: http://blog.thecodewhisperer.com/2013/01/29/consequences-of-dependency-inversion-principle/

      The point is, Dependency Injection doesn’t need xml or annotations or anything. However, an IoC framework usually does. But there are alternatives. I.e.. Tapestry uses Module classes for configuration.

  2. Pingback: Practical examples to dependency injection | tamasrev

  3. Pingback: Lessons learned from the IoC framework comparison. | tamasrev

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s