Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Thursday, September 11, 2008

Tapestry 5 IoC: Introducing Service Configurations

In the previous article, I discussed the basics of Tapestry 5 IoC. I focused on the terseness of Tapestry's container, even though everything occurs in Java code. I alluded to special features of Tapestry 5 IoC, service configurations. Let's start investigating those.

In traditional dependency injection, the relationship between a service and its dependencies is many-to-one: many services may inject a specific dependency. Whether that dependency is selected just by service type, or by service id, or by some other mechanism, it's still one single object.

Service configurations are somewhat inverted: they are a relationship from one service to many objects. The objects, or contributions, may be simple objects or may themselves be services.

Let's use a specific example from Tapestry to put this into perspective. Previously I showed the service builder method for the TranslatorSource service:

public static TranslatorSource buildTranslatorSource(ComponentInstantiatorSource componentInstantiatorSource, 
  ServiceResources resources)
{
    TranslatorSourceImpl service = resources.autobuild(TranslatorSourceImpl.class);

    componentInstantiatorSource.addInvalidationListener(service);

    return service;
}

Let's dive a little deeper and look at what this service does. It's a source for Translator objects, which are an integral part of Tapestry's HTML form support. Translators are responsible for converting between server-side values (such as numbers, dates, and so forth) and client-side strings. They also play a role in client-side validation of user input.

Tapestry matches up properties that are edited by TextFields with corresponding Translator instances. This all happens inside the TextField component and is largely invisible to programmers. In any case, the TranslatorSource service is central:

public interface TranslatorSource
{
    /**
     * Returns the translator with the given logical name.
     *
     * @param name name of translator (as configured)
     * @return the shared translator instance
     * @throws RuntimeException if no translator is configured for the provided name
     */
    Translator get(String name);

    /**
     * Finds a {@link Translator} that is appropriate to the given type, which is usually obtained via {@link
     * org.apache.tapestry5.Binding#getBindingType()}. Performs an inheritanced-based search for the best match.
     *
     * @param valueType the type of value for which a default translator is needed
     * @return the matching translator, or null if no match can be found
     */
    Translator findByType(Class valueType);

    /**
     * Finds a {@link Translator} that is appropriate to the given type, which is usually obtained via {@link
     * org.apache.tapestry5.Binding#getBindingType()}. Performs an inheritanced-based search for the best match.
     *
     * @param valueType the type of value for which a default translator is needed
     * @return the matching translator
     * @throws IllegalArgumentException if no known validator matches the provided type
     */
    Translator getByType(Class valueType);
}

Here's where it gets interesting

So, what Translators are built into Tapestry? You might think you could tell by looking at the implementation of the service:

public class TranslatorSourceImpl implements TranslatorSource, InvalidationListener
{
    private final Map<String, Translator> translators = CollectionFactory.newCaseInsensitiveMap();

    private final StrategyRegistry<Translator> registry;

    public TranslatorSourceImpl(Collection<Translator> configuration)
    {
        Map<Class, Translator> typeToTranslator = CollectionFactory.newMap();

        for (Translator t : configuration)
        {
            translators.put(t.getName(), t);
            typeToTranslator.put(t.getType(), t);
        }

        registry = StrategyRegistry.newInstance(Translator.class, typeToTranslator, true);
    }

    public Translator get(String name)
    {

        Translator result = translators.get(name);

        if (result == null)
            throw new RuntimeException(ServicesMessages.unknownTranslatorType(name, InternalUtils
                    .sortedKeys(translators)));

        return result;
    }

    public Translator getByType(Class valueType)
    {
        Translator result = registry.get(valueType);

        if (result == null)
        {
            List<String> names = CollectionFactory.newList();

            for (Class type : registry.getTypes())
            {
                names.add(type.getName());
            }

            throw new IllegalArgumentException(ServicesMessages.noTranslatorForType(valueType, names));
        }

        return result;
    }

    public Translator findByType(Class valueType)
    {
        return registry.get(valueType);
    }

    public void objectWasInvalidated()
    {
        registry.clearCache();
    }
}

But you don't see any pre-defined Translator instances here ... just Collection<Translator> configuration passed to the constructor. Each Translator provides its name, and those all go into the translators map ... but the question is, where do they come from?

Jumping back to TapestryModule, we see a likely method:

public static void contributeTranslatorSource(Configuration<Translator> configuration)
{
    configuration.add(new StringTranslator());
    configuration.add(new ByteTranslator());
    configuration.add(new IntegerTranslator());
    configuration.add(new LongTranslator());
    configuration.add(new FloatTranslator());
    configuration.add(new DoubleTranslator());
    configuration.add(new ShortTranslator());
}

It's looking pretty likely that Tapestry supports string, byte, integer, long, float, double and short out of the box. The naming of this method is another example of convention over configuration. The prefix this time is contribute and the rest of the method name matches the service id, TranslatorSource.

The Configuration object has a single method, add():

public interface Configuration<T>
{
    /**
     * Adds an object to the service's contribution.
     *
     * @param object to add to the service's configuration
     */
    void add(T object);
}

So, Tapestry has invoked the contributeTranslatorSource() method, collected up the objects, the Translators, added to the configuration object, and converted the configuration object to a Collection, which is ultimately passed to the TranslatorSourceImpl constructor.

Seems awfully complicated, doesn't it? Well, it is nice (from a testing perspective) that TranslatorSourceImpl isn't tied to any particular implementations of Translator. But that's not the real benefit.

The real benefit, and this is the basis of the entire concept, is that you are not locked into just these Translators. You can define your own, and mix them in with the ones supplied by Tapestry. And you don't have to hack TapestryModule or TranslatorSourceImpl to do it.

Say your application defines a Currency class, to track currency amounts of orders and payments. That might be handy to use instead of double, for accuracy reasons. You might also want to parse and format currency values differently than naked doubles ... for example, to require exactly two digits of precision, or to ignore a leading dollar sign.

To mix in your own Translators, all you need to do is define your own module:

public class AppModule
{
  public static void contributeTranslatorSource(Configuration<Translator> configuration)
  {
    configuration.add(new CurrencyTranslator());
  }
}

This translator, CurrencyTranslator, will be indistinguishable from the default set of Translators provided by Tapestry; TranslatorSourceImpl will have no way of telling which Translators came from where. Your contributions are on an even footing with those provided by Tapestry itself.

You might ask in what order are these contribute methods are invoked? The answer is: Who knows? That's why the configuration is passed to the service implementation as (unordered) Collection, not (ordered) List. As we'll see in the next article, Tapestry has alternatives for when you care about ordering, or when you want your configuration in the form of a Map.

When are these methods called? They are called when the TranslatorSource service is realized, which happens when a method of the service is first invoked. Tapestry IoC is by default very lazy, it doesn't instantiate services until necessary. The service's proxy is responsible for this realization process, and its done in a thread-safe manner. Dealing with this kind of loose binding in a structured manner is very helpful: it means that the TranslatorSource service is simplified: it doesn't need a method to add a new Translator, there's fewer issues related to multiple threads, and the available set of Translators never changes, which makes the behavior of the service much more predictable.

Conclusion

We've only just pierced the surface of Tapestry configurations, but we're beginning to see what I mean when talk about Tapestry's extensibility. Much of the key behavior of Tapestry is specified in terms of this kind of configuration, or one of its close relatives. And, as the example showed, building a service that uses a configuration is just a matter of defining a parameter of type Collection in the service's constructor.

In future articles, I'll discuss other variations of service configurations, and show how to go meta with Tapestry by leveraging configurations in combination with service-building services!

Monday, September 08, 2008

Closures coming for Objective-C

Apparent, closures are coming for C and Objective-C. Objective-C was my first OO programming language, and the transition to Java was a painful one. For a long time, I couldn't go back because Objective-C was not garbage collected (this has changed). Then I couldn't go back because it didn't have closures (or even closures' red headed step child: the inner class).

But Objective-C with garbage collection and true closures? That's getting somewhere, fast.

And no I'm not rewriting Tapestry in Objective-C. You know that question was coming!

IntelliJ: Maven improvements in 7.0.4

I haven't updated my project dependencies in a bit since upgrading from IntelliJ 7.0.3 to 7.0.4. I was pleasantly surprised after updating my POM dependencies that the Maven support has improved. In the past, the Maven support had a tendency to select less than ideal values for each module's source paths ... and it would overwrite my customizations every time I did a sync. I had come to dread changing dependencies because of this.

In 7.0.4 I had to refresh my Maven projects list; this took a few minutes. But lo and behold; it did a perfect job, not altering my personal changes. Bravo!

Sunday, September 07, 2008

And the release candidate sprint is on!

Just rounding out the last few important bug fixes (and enhancements) that are needed for the Tapestry 5 release candidate. Can I get it together before I leave for my Europe trip on Sept. 15th? If not, the RC may wait until October.

Importantly, I think I've nailed TAPESTRY-2561, the thread deadlock bug.

I'm already looking forward to 5.1: I have lots of fully backwards compatible enhancements to make.

Java's jconsole to the rescue

If you find yourself doing something tricky with threads, such as rooting out some insidious thread deadlocks, jconsole is invaluable. Having it analyze the threads to get the deadlocks is awesome, and the stack trace even includes identifiers about what object monitors have been locked. Way better than sorting through all that, by hand, from a text stack dump.

Selenium tests just started hanging? Don't Panic!

So, here I am, in the middle of some intense debugging related to the dreaded combination of threads, class loaders and deadlocks and suddenly ... my integration tests no longer run!

Ultimately, my changes were very modest (a little bit of extra synchronization against the context class loader), so what gives?

Well, if your tests are like mine, and run against Firefox, your issue might be that Selenium is unable to start up Firefox if an upgrade has occurred, especially if the Firefox upgrade is not compatible with your plugins.

The solution? Start Firefox manually, to get through the dialogs it presents. Then rerun your tests.

Friday, August 29, 2008

Id conflict inside Web Browser

Just spent many minutes on a wild goose chase and the underlying cause was that I had a <div> and a <textarea> that shared the same id. $(id) returned the <div>, most likely because it occured first in the document. I then got an error because <div> doesn't response to activate() ... that's a Prototype method attached to form elements.

I would be tempted to make Tapestry enforce this (that the id attribute for any node was unique), but that won't proof it against partial renders under Ajax, so I don't know that there's any point.

What's really going on is that CSS rules based on element id rather than element class are evil, and should only be used as a last resort. The free CSS template I got off the web uses too many explicit ids where it could be using CSS classes. You get what you pay for.

Tuesday, August 26, 2008

Tapestry Europe Tour

I'm going to be in Oslo, Norway for JavaZone from Sep. 16 to Sep. 19th. I'm presenting on Thursday afternoon.

Next up, a week of Tapestry training in London.

I then fly to Amsterdam on Sep. 27th, for three more days of Tapestry training and then some vacation.

Next, down to Paris just for vacation before returning home on October 10th.

Should be a lot of fun, and a chance to meet with new and future Tapestry users. I can't wait!

Tapestry 5 IoC: Binding and Building Services

Tapestry 5 includes its own internal Inversion of Control container. This is often a point of contention ... why not just use Spring or (in more recent conversations) Guice?

That's a complex question; simply put, Tapestry has requirements as a framework that the other containers don't offer solutions to.

This posting is a simple introduction to the basics of Tapestry 5 IoC. In later postings, we'll get into more detail about the advanced features of Tapestry's IoC container, the ones that really distance it from Spring and Guice.

Tapestry uses the term "service" for the primary objects that it manages for you. Spring uses the term "bean". A service is normally an interface and a class that implements the interface. In the most typical case, only a single service implements the interface, but T5 IoC is fully capable of handling the case where one service interface has a number of distinct services; even the case where a single class is instantiated with a different configuration.

Every service has a unique id string. In most cases, this is just the simple name of the service interface. When the same interface is used by multiple services, you will have to identify the service id explicitly.

To keep things real, I'll use actual, though abbreviated, examples from Tapestry's code base.

T5 IoC uses module classes to identify what services are available. A module class is a POJO class with a special method on it, a method named bind(). A Tapestry application will consist of a number of modules: some modules provided by Tapestry itself, some by third party libraries or extensions, and some by the application itself. Tapestry mixes and matches all of this information, all of the services defined by each of the modules, into a single registry of services.

That may sound more complex than it really is. The reality is that in the bind() method, we simply match service interfaces to corresponding implementations:

public final class TapestryModule
{
    public static void bind(ServiceBinder binder)
    {
        binder.bind(ClasspathAssetAliasManager.class, ClasspathAssetAliasManagerImpl.class);
        binder.bind(PersistentLocale.class, PersistentLocaleImpl.class);
        binder.bind(ApplicationStateManager.class, ApplicationStateManagerImpl.class);
        // ... and so on
    }
}

The ServiceBinder is uses generics to ensure that the class you specify implements the service interface. The API is a fluent interface: you can chain a few extra method calls onto bind() to override defaults, for example:

      binder.bind(ObjectProvider.class, AssetObjectProvider.class).withId("AssetObjectProvider");

TapestryModule actually defines quite a few additional services.

Let's look at an example:

public interface PersistentLocale
{
    void set(Locale locale);

    Locale get();

    boolean isSet();
}

I've stripped out the comments to save space ... but this service manages the user's locale; it's a key part of Tapestry 5's localization support. The implementation we'll see shortly works using HTTP Cookies, but that isn't important to the code that uses PersistentLocale.

public class PersistentLocaleImpl implements PersistentLocale
{
    private static final String LOCALE_COOKIE_NAME = "org.apache.tapestry5.locale";

    private final Cookies cookieSource;

    public PersistentLocaleImpl(Cookies cookieSource)
    {
        this.cookieSource = cookieSource;
    }

    public void set(Locale locale)
    {
        cookieSource.writeCookieValue(LOCALE_COOKIE_NAME, locale.toString());
    }

    public Locale get()
    {
        String localeCookieValue = getCookieValue();

        return localeCookieValue != null ? LocaleUtils.toLocale(localeCookieValue) : null;
    }

    private String getCookieValue()
    {
        return cookieSource.readCookieValue(LOCALE_COOKIE_NAME);
    }

    public boolean isSet()
    {
        return getCookieValue() != null;
    }
}

T5 IoC does all injection through the constructor. This is to encourage you to write your dependencies into final fields, which is thread safe. Typically, your services will be immutable objects: all fields final.

PersistentLocaleImpl has a dependency on another service, Cookies. And what is Cookies? It's another service interface. Notice that we don't have to do any extra configuration here ... since there's one, and only one, service that implements the Cookies interface, that's all the information Tapestry needs to wire things together.

Other service implementations inside Tapestry have as few as zero dependencies, and as many as eight. There's no theoretical limit, it's just that having more than a few dependencies is a design smell ... that you can break things into smaller pieces.

One of the hallmarks of coding using an IoC container is this level of terseness, also knows as passing the buck. Given that PersistentLocaleImpl is concerned with HTTP cookies, you'd think that it would, somehow, get ahold of the HttpServletRequest object and start invoking getCookies() and addCookie() on it ... but instead, all the details of interfacing with the Servlet API and the rather awkward API for HTTP cookies is swept into a corner, inside the Cookies service implementation.

That's great ... it makes the implementation of PersistentLocaleImpl (as well as any other code that happens to care about HTTP cookies) that much simpler and easier to test.

Service Lifecycle

Tapestry services have a specific lifecycle:

defined
Identified via the ServiceBinder, but not yet referenced
virtual
A proxy exists that has been injected as a dependency of some other service, but no methods of the proxy have been invoked
realized
The service has been instantiated with dependencies

The beauty of this is that your code is completely unaware of this; all the work inside Tapestry ... creating proxies, realizing service implementations, occurs in a lazy but thread-safe manner. It's as if all the services are instantiated at startup without taking the time to actually do that work.

Again, the appeal of an IoC container is that you get to break your application into tiny, easily tested bits, and the IoC container is responsible for connecting everything back together at runtime. It really leads to a new way of coding, and thinking about coding.

Service Builder Methods

Sometimes just instantiating a class is not enough; there may be additional configuration needed as part of instantiating the class. Tapestry 5 IoC's predecessor, HiveMind, accomplished such goals with complex service-building services. It ended up being a lot of XML.

T5 IoC accomplishes the same, and more, using service builder methods; module methods that construct a service. A typical case is when a service implementation needs to listen to events from some other service:

public static TranslatorSource buildTranslatorSource(ComponentInstantiatorSource componentInstantiatorSource, 
  ServiceResources resources)
{
    TranslatorSourceImpl service = resources.autobuild(TranslatorSourceImpl.class);

    componentInstantiatorSource.addInvalidationListener(service);

    return service;
}

Module methods prefixed with "build" are service builder methods. The service interface is defined from the return value (TranslatorSource). The service id is explicitly "TranslatorSource" (that is, everything after "build" in the method name).

Here, Tapestry has injected into the service builder method. ComponentInstantiatorSource is a service that fires events. ServiceResources is something else: it is a bundle of resources related to the service being constructed ... including the ability to instantiate an object including dependencies. What's great here is that buildTranslatorSource() doesn't need to know what the dependencies of TranslatorSourceImpl are, it can instantiate the class with dependencies using the autobuild() method. The service builder then adds the new service as a listener of the ComponentInstantiatorSource, before returning it.

This is a great separation of concerns: we have a construction concern (being an event listener) that's distinct from the operational concerns of TranslatorSource. And they are kept separate.

Conclusion

Tapestry IoC has simple and concise API for defining services and, in most cases, handles dependencies automatically. The end result is that it becomes child's play to divide-and-conquer: convert old, monolithic, hard to maintain code into small, easily tested, easily understood services.

In future postings, I'll go into more detail about the more advanced features of Tapestry: service scopes, service configurations and service decorations.

Ajax and Selenium: waitForCondition()

Selenium is a very useful tool but it can be very, very obtuse.

One challenge is dealing with Ajax; you might click on a button, but without a full page refresh, it's hard to know when to look for expected changes via Ajax and DHTML.

In the past, my test suites had short sleeps, a few hundred milliseconds. This makes them fail sporadically ... every once and a while on my MacBook Pro I'm doing so much other stuff while the tests run that the timing goes screwy.

You're then left with a difficult choice: sleep too short and the tests may fail. Sleep too long and your tests will always be slow.

Fortunately, there's a third option: Selenium's waitForCondition call. Of course, their documentation is worthless.

What it is supposed to do is evaluate a JavaScript snippet repeatedly, until the snippet returns true. However, it's tricky to get right. Like much in JavaScript, it's about context.

In my case, I wanted to wait for a client-side popup <div> to appear:

        type("amount", "abc");
        type("quantity", "abc");

        click(SUBMIT);

        waitForCondition("document.getElementById('amount:errorpopup')", "5000");

        assertText("//div[@id='amount:errorpopup']/span", "You must provide a numeric value for Amount.");
        assertText("//div[@id='quantity:errorpopup']/span", "Provide quantity as a number.");

JavaScript treats null as false, and getElementById() returns null if an element with the id does not exist.

I'm making the assumption that once one of two <div> elements appears, they both will. I then use some XPath to get the text inside the <span> inside each <div>, to make sure the correct message was displayed to the user.

But this code doesn't work.

The problem is that document isn't what you'd expect; I'm guessing that it's some other frame inside the browser (Selenium's UI and code executes in one frame, which runs the actual application inside the second frame).

The solution took some research and the sacrifice of a few small furry animals to obtain:

        waitForCondition("selenium.browserbot.getCurrentWindow().document.getElementById('amount:errorpopup')", "5000");

That works, and it works much faster than adding a Thread.sleep() in the middle of my code.

Tapestry @ JavaZone 2008

I'll be presenting on Tapestry 5 at JavaZone. I'll be in Lab 2, on Sept 18th, at 2:15. With less than an hour to talk about Tapestry 5 and compare it briefly to Wicket and Rails ... well, I'll be talking fast!

I also just noticed that there's a session on comparing IoC containers that includes Tapestry 5 IoC. That's Lab 6, on the 17th, at 5pm.

Friday, August 22, 2008

Stupid DateFormat

Working on some bugs related to the DateField component. Did you know that (for US English, anyway) DateFormat.getDateInstance(DateFormat.SHORT) provides back a DateFormat equivalent to "M/d/yy"?

That's a problem, because it has a tendency to format Dates to be two digit years. When it parses those same strings, it ends up considering the date to be in the first century AD.

Thus in this code:

  Date date1 = new Date(...);
  Date date2 = format.parse(format.format(date1));

date2 may or may not be the same as date1. Certainly any time information will be stripped out (this is expected), but often date2 will be in the wrong century.

I'm working right now to get the correct localized DateFormat, but ensure that the year portion is four digits, not two.

Thursday, August 21, 2008

Joshua Considers Tapestry 5

Josua Partogi has put together a nice quick intro to Tapestry 5. My only complaints are that the formatting makes the blog posting a bit hard to read, and that he included a lot of extra stuff in AppModule (put there by the Tapestry quickstart archetype) that's not required, such as the request timing filter. The archetype adds that as an example of what you can do. Finally, he uses the term "template" where all other documentation calls that type of component a "layout" (or, in Tapestry 4 terms, a "border").

Wednesday, August 13, 2008

My Ideas for Java Closures

According to Neal Gafter, the story for closures it still wide open.

As someone who writes a lot of code using closure-like mechanisms ... in the form of lots of inline inner classes ... I have a few idea of what I want in a solution. I think I'm writing some powerful and elegant code today, but that elegance in function is undermined by some severe awkwardness in its expression as Java code.

It really comes down to conciseness. I can accomplish pretty much everything I need using inner classes and holder objects, such as AtomicInteger and friends. But it ends up being more code than I'd like.

What I want (to borrow Stu's term) is to emphasize the essence of my logic, and strip away the ceremony: the naming of the interface (it should be known from context), the types of parameters (just the names, please), the list of thrown exceptions, etc.

First of all, let's constrain the problem. Closures would be defined in terms of an interface, an interface that contains a single method. Attempting to use the concise syntax with an interface that contains multiple methods would simply be a compiler error.

Second, the closure block should have free read/write access to parameters and local variables in the enclosing method. This can easily be accomplished with syntactic sugar: the variables can be converted into references to holder objects, such as AtomicInteger, that are stored on the heap. Today, shared fields must be final, to indicate that it is safe to share references to the object between the main method and any inner classes.

The hard part about sharing local variables in this way isn't implementing the syntactic sugar, it's about the updating of the information provided to the debugger so that the debugger can undo the syntactic sugar changes, and make variables in the enclosing method, or in the closure block, appear to be local.

Lastly, syntax. I think Groovy has the right syntax here. The important part is for the compiler to actually help out rather than for it to complain from the side-lines. Today's Java compiler has all the type information, but just uses that to build fancy error messages about what you should have typed. It should be using that type information to avoid the necessity of all the extra typing (that is, keyboard entry, not the need for types in the Ruby/Groovy sense of the word).

When a closure is passed to another method, the parameter type determines all the compiler should need to know. Likewise, when a closure is assigned to a variable, the variable defines the closure interface. Thus:

List<Widget> widgets = ... ;
Collections.sort(widgets,
  { w1, w2 -> return w1.getWidgetId().compareTo(w2.getWidgetId()); });

The generic type of the list, Widget, informs the generic type of the Comparator. Thus w1 and w2 are fully typed, as instances of Widget. The return type of the closure is pegged as int (also from the Comparator interface).

Side note: I'd also like to see a lot of other streamlining of Java, such as a Groovy-style implicit return.

In other words, the above could be expanded by the compiler to the following in terms of compilation:

List<Widget> widgets = ... ;
Collections.sort(widgets,
  new Comparator<Widget>()
  {
    public int compare(Widget w1, Widget w2)
    {
       return w1.getWidgetId().compareTo(w2.getWidgetId());
    }
  });

See, the essence is still there, the comparison of the widget Ids. But its now occluded by all that ceremony about interface names, method names, return values, generic types, and so forth.

A caveat: there are edge cases where we'll need to identify the closure interface type. This occurs when a method to be passed a closure is overridden. The compiler should be able to reduce the candidates based on parameter count, checked exceptions thrown inside the closure, and other factors ... but it may be necessary to implement an alternative syntax. For example:

ExecutorService service = ...;
Future<?> = service.submit(Runnable { performExpensiveOperation(); });

submit() is overridden to accept a Callable as well as a Runnable. Callable can return a value. Again, minimal syntax here: the interface name followed by the implementation of the closure method. This compares to the Groovy as keyword.

Now, it's easy to get carried away and put in stuff like Python style co-routines (the yield keyword), or want a syntax to allow the closure to force a return value from the enclosing block or method. <sarcasm> Yep, let's add a few more alien concepts to the language, people love that. </sarcasm>.

These are also not function objects, so you can't easily do magic things such as currying (currying is a way of pre-supplying some of the parameters to a function, such that a new function is created that takes fewer parameters). An "interface" that's curried is a whole new interface and that's OK by me.

I'm more more modest. I don't particularly want to add new features to the Java language, just new syntax for the compiler, to let it do the ugly plumbing. That's kind of my theory for Tapestry's relationship to Java and the Servlet API as well, and it works.

A few side notes:

Annotations could be used to help the compiler out. For example, if a common interface should be a closure, but has multiple methods, an annotation could be used to tell the compiler which method of the interface is applicable to use as a closure; any other methods (in the inner class) would be no-ops or (perhaps guided by additional annotations) failures.

Annotations on fields and parameters (or perhaps on the method) could help the compiler decide how to share visible variables: do we need to use a thread-safe approach (such as something based on AtomicReference) or simple, non-synchronized holder objects? The compiler could do some escape analysis as well to chose the best solution, but in many cases, it's on the coder's shoulders to make the right decision.

From what I can tell, my ideas are closest to the Concise Instance Creation Expressions proposal by Bob Lee, Doug Lea, and Josh Bloch. However, in my opinion, the compiler can do much more in terms of providing type information. I think CICE stumbles in that it allows for creation of classes, not just interfaces, and gets bogged down in defining closure types and parameter types ... again, things that (with the mandate of single-method interfaces), the compiler can do autonomously.

Likewise, CICE requires that variables visible to the inner block be marked "public". This to me is something that the compiler can analyze; it can identify any assignments to variables and promote such variables to stored-on-the-heap status. I don't see the need for final; the compiler has plenty of ability to determine if a parameter or local variable is ever updated within the body of a method (and the body of any inner classes or closures of that method).

My basic concept is: Less is More. Support fewer cases but do so more cleanly, more concisely, and more understandably. Simplify. Let the compiler do more work. Reduce the density and complexity of the Java code. Expose the essence.

Thursday, August 07, 2008

In Portland, even the cafes are Scalable

Portland is a town where, while walking to the gym, you can come across someone sitting at a sidewalk table of a local cafe, leafing through a hard-copy of Programming in Scala. We chatted for a moment (even though I was not quite awake yet). One more thing for me to do ... check out Lift.

And, yes, he actually had a print shop print and bind the PDF (as the book is only available electronically right now).

Wednesday, August 06, 2008

Coming to London, UK

It's looking very likely I'll be in London, UK next month for a bit of on-site training. I'm also speaking at JavaZone 2008. I'd love to optimize the number of flights I make to Europe, so if anyone is on the Continent is hankering for a little Tapestry training, now's a good time to get in touch!

After London, Suzy and I are then going to do a little trip around Europe, taking a couple of days in Paris and Amsterdam.

Latest FireBug understands redirects

Huzzah! One of the problems I've had using FireBug is that it didn't record a component event request long enough for me to see it; all I'd see was the follow-up page render request. But the latest FireBug is showing me the component event and the 302 response, and then the page render request, which gives a much more accurate idea of what is going on and how long it takes.

Thursday, July 31, 2008

Blast from the Past: Tapestry 3.0.5

My current task has put me in an odd mood. A client of Jesse's (Kuhnert) contracted him to make some changes to the Tapestry 3 code base, with me auditting those changes. Jesse has backported some ideas from Tapestry 4 and Tapestry 5: mostly the use of a backported version of java.util.concurrent to improve Tapestry 3 throughput by replacing single-thread synchronized access with concurrent access.

Many of these files haven't really been touched since 2004. It's brought back a lot of memories, such as working on TheServerSide's application.

To my jaundiced eye, the code looks almost primitive; no IoC container, no dependency management, heavy reliance on base classes, a lot of rather naive multithreading.

Overall, I'd say that Tapestry 5 is as far advanced over Tapestry 3 as Tapestry 3 was advanced over Struts. Probably more so. Looking back over this code has given me some perspective.

Thursday, July 24, 2008

IntelliJ: Paste...

screenshot I discovered this one on the flight home. The Paste ... menu item (Shift-Command-V) raises up a window of recently copied selections on the clipboard.

Handy!

Making Java EE Suck Less

Ben Gidley has posted a medium length blog praising Tapestry 5. He took an interesting tack ... describing the requirements of a useful, useable web framework ... in terms of what Tapestry 5 does. I especially liked "Not require a large bespoke framework to be assembled around the framework."

He ends with:

This is the framework that could save J2EE from being a 'dull' and slow corporate tool and eventually fading as other toolsets come and replace it.

This, to me, hearkens back to how Dave Thomas defined Ajax, early on: It's whatever makes the web browser suck less.

To paraphrase, Tapestry is what makes the entire Java EE platform suck less.

Last week, when I was training in Cambridge, UK I saw a bit of this. The client was gearing up to create a full REST interface around their database access. They were starting to evaluate servers and APIs and think about integrations as well as how to represent object hierarchies and updates inside an XML or JSON structured REST query and response ... and how many more Java representations of entity objects would be required.

... but after working on the examples in the Tapestry Workshop they have abandoned the REST approach (until some external system actually needs access). Instead, they are modeling more of their application on the same light principals as the Tapestry Workshop:

  • Inject the Hibernate Session directly into pages, to perform simple queries
  • Use the @CommitAfter annotation to handle update operations
  • Move common queries (used on multiple pages) to a central, injectable service

This way they are able to leverage a single Java object representation of their database entities, as managed by Hibernate. They can use Hibernate features such as lazy loading and update cascading (things that would be prohibitively more difficult if there were a REST layer between the presentation tier and Hibernate) . This is all possible because Tapestry isn't just a web framework, it's also, because of Tapestry IoC, an integration platform. This allows the integration with Hibernate to be "light and tight".

Wednesday, July 09, 2008

Coming to Cambridge, UK

I'll be flying into Cambridge, UK for a week of Tapestry training. I'll be there from Sunday through Thursday night before returning to sunny Portland on Friday. Drop me a line if you are a Tapestry enthusiast in the area ... it's always fun to get together for a drink and talk tech.

Thursday, June 26, 2008

IntelliJ: Flip Equals

Just hit a NullPointerException in some code:

    public boolean isOwner()
    {
        return authManager.getUser().equals(blog.getOwner());
    }

Turns out, sometimes getUser() returns null. I started to retype this, then thought: "Can IntelliJ help me?"

Answer: yes. Because of IntelliJ coolness, I click anywhere in the expression, type option-enter and choose 'Flip .equals()' and it rewrites the code to:

    public boolean isOwner()
    {
        return blog.getOwner().equals(authManager.getUser());
    }

Twitku

I just noticed that Ted Neward is on Twitter ... but only twits in Haiku form.

Monday, June 23, 2008

Maven for dependencies, not building

I've been working a bit with Ivy but finding it doesn't quite fit my needs (or that I have a lot more to learn). What I've found with Ivy is that it has its way of doing things, and part of The One True Path is that you should create a repository just for your organization ... they really hate the idea of depending on the central Maven repository.

In any case, my need is to make it easy to build Tapestry or Tapestry demos from source without a lot of fuss. That's where I like Maven's dependency management, especially for the most common artifacts.

The other problem I've had with Ant is that it doesn't get Maven scopes perfectly, or Maven artifacts (such as source JARs) without a lot of redundant typing.

So, for the mean time, I'm back to Ant plus the Maven Ant tasks.

What I want is to create and manage four folders:

  • runtime
  • runtime-src
  • test
  • test-src

The main folders, runtime and test, contain JARs needed for compilation/execution of the production code, and for the test code. This could expand to include more of Maven's scopes in the future, such as provided, but it suits my current needs. The -src folders contain source JARs, which I find essential to being productive. Nothing irks me more than opening a base class or interface and getting that ugly view of just the method names because source isn't available. It gives me flashbacks to miserable sessions with WebLogic trying to figure out what the hell it was doing.

So, how can we get there without the rest of Maven? How about this:

<?xml version="1.0"?>
<project name="common" xmlns:mvn="urn:maven-artifact-ant">

    <!-- Names of common directories used in the build. -->

    <!-- Directory in which temporary files are created. -->
    <property name="target.dir" value="target"/>
    <property name="target.lib.dir" value="target/lib"/>

    <!-- Directory to which imported dependencies are placed. Four subfolders are created: runtime, runtime-src, test
         and test-src. -->
    <property name="lib.dir" value="lib"/>

    <path id="maven-ant-tasks.classpath" path="maven-ant-tasks-2.0.9.jar"/>

    <typedef resource="org/apache/maven/artifact/ant/antlib.xml" uri="urn:maven-artifact-ant"
             classpathref="maven-ant-tasks.classpath"/>


    <macrodef name="import-dependencies">
        <attribute name="scope"/>
        <attribute name="folder" default="@{scope}"/>
        <element name="dependencies" implicit="true"/>

        <!-- locals -->
        <attribute name="lib" default="${lib.dir}/@{folder}"/>
        <attribute name="lib-src" default="@{lib}-src"/>

        <attribute name="target-lib" default="${target.lib.dir}/@{folder}"/>
        <attribute name="target-lib-src" default="${target.lib.dir}/@{folder}-src"/>

        <attribute name="lib-fileset" default="@{scope}.dependency.fileset"/>
        <attribute name="src-fileset" default="@{scope}.dependency.sources.fileset"/>

        <sequential>
            <mvn:dependencies pomrefid="project.pom" verbose="true" usescope="@{scope}"
                              filesetid="@{lib-fileset}"
                              sourcesfilesetid="@{src-fileset}">
                <remoteRepository id="tapestry-nightly"
                                  url="http://tapestry.formos.com/maven-snapshot-repository">
                    <snapshots enabled="true"/>
                </remoteRepository>
                <remoteRepository id="maven-central"
                                  url=" http://repo1.maven.org/maven2"/>
                <remoterepository id="openqa-release"
                                  url="http://archiva.openqa.org/repository/releases/"/>
            </mvn:dependencies>

            <mkdir dir="@{target-lib}"/>
            <mkdir dir="@{target-lib-src}"/>
            <!-- Flatten them, so we can do a sync. -->
            <copy todir="@{target-lib}" flatten="true" preservelastmodified="true">
                <fileset refid="@{lib-fileset}"/>
            </copy>

            <copy todir="@{target-lib-src}" flatten="true" preservelastmodified="true">
                <fileset refid="@{src-fileset}"/>
            </copy>


        </sequential>
    </macrodef>

    <macrodef name="remove-overlap">
        <attribute name="source"/>
        <attribute name="target"/>

        <attribute name="fileset.property" default="@{source}.overlap.files"/>

        <sequential>
            <pathconvert pathsep="," property="@{fileset.property}">
                <fileset dir="@{source}"/>
                <flattenmapper/>
            </pathconvert>

            <delete dir="@{target}" includes="${@{fileset.property}}"/>
        </sequential>
    </macrodef>

    <target name="setup-libs" description="Copy dependencies to lib folder.">

        <mvn:pom file="pom.xml" id="project.pom"/>

        <!-- Delete the scratchpad space. -->
        <delete dir="${target.lib.dir}" quiet="true"/>

        <import-dependencies scope="runtime"/>

        <!-- For the moment, this is somewhat broken: test scope is a super-set of compile/runtime scope.
             Everything in the runtime folders will end up in the test folders, plus more. -->
        <import-dependencies scope="test"/>


        <!-- Snapshots come down with the datestamp/version number, we need to fix that. -->
        <move todir="${target.lib.dir}" preservelastmodified="true">
            <fileset dir="${target.lib.dir}"/>
            <!-- Turn the date/version stamp back into SNAPSHOT -->
            <regexpmapper from="^(.*)(\d{8}\.\d{6}\-\d+)(.*)$$" to="\1SNAPSHOT\3"/>
        </move>

        <!-- Delete the overlap between lib/runtime and lib/test -->

        <remove-overlap source="${target.lib.dir}/runtime" target="${target.lib.dir}/test"/>
        <remove-overlap source="${target.lib.dir}/runtime-src" target="${target.lib.dir}/test-src"/>

        <echo>*** Synchronizing ${lib.dir} ...</echo>
        <sync todir="${lib.dir}" verbose="true">
            <fileset dir="${target.lib.dir}"/>
        </sync>
        <echo>... done.</echo>

        <delete dir="${target.lib.dir}" quiet="true"/>
    </target>

</project>

Just add the Maven Ant tasks JAR and a pom.xml (that exists exclusively to identify dependencies) and you are off and running.

Some of my requirements are awkward to implement in Ant:

  • Flattened directories; Maven really wants to build a mirror of the repository, with directories for groups, but I want everything in a single directory.
  • Remove overlap; Maven's test scope includes the runtime scope, extra work is necessary to keep the test directory to just the additional JARs without redundantly including what's already in runtime
  • Timestamps; I want to sync the directories, not just copy in, to account for version number changes and I don't want to change timestamps unless a file has changed (to keep the IDE from wasting time rescanning and reparsing it)

The script creates a temporary directory under target, which is first used to flatten the copy from the local repository, then to remove the runtime vs. test redundancies before sync-ing it over the proper lib directory.

The best part of all this? Nothing happens until ant setup-libs rather than the frequent surprises that Maven gives you!

Eventually, I'll repackage some of this into a common build.xml that can be shared across modules. In the meantime, it's good enough for demos and labs in the workshop.

Thursday, June 19, 2008

Firefox 3 : The Good, the Bad

The Good is that it does feel faster,and it now sports a UI that looks much more Mac native.

The Bad is that many plugins don't work yet, such as FireBug! In addition, the Selenium support for Firefox is glitchy with the new release.

Update: People are coming out of the woodwork to point me at the Firebug for FF3 ... check the comments!

Wednesday, June 11, 2008

Groovy: Leaky Abstractions and Confusing Exceptions

Here's an odd place to get a ClassCastException:

  • org.hibernate.type.StringType.toString(StringType.java:44)
  • org.hibernate.type.NullableType.nullSafeToString(NullableType.java:93)
  • org.hibernate.type.NullableType.nullSafeSet(NullableType.java:140)
  • org.hibernate.type.NullableType.nullSafeSet(NullableType.java:116)
  • org.hibernate.param.NamedParameterSpecification.bind(NamedParameterSpecification.java:38)
  • org.hibernate.loader.hql.QueryLoader.bindParameterValues(QueryLoader.java:488)
  • org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1554)
  • org.hibernate.loader.Loader.doQuery(Loader.java:661)
  • org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224)
  • org.hibernate.loader.Loader.doList(Loader.java:2211)
  • org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2095)
  • org.hibernate.loader.Loader.list(Loader.java:2090)
  • org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:375)
  • org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:338)
  • org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:172)
  • org.hibernate.impl.SessionImpl.list(SessionImpl.java:1121)
  • org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)
  • org.hibernate.impl.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:811)
  • sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  • sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
  • sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
  • java.lang.reflect.Method.invoke(Method.java:585)
  • org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
  • groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
  • groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:912)
  • groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:756)
  • org.codehaus.groovy.runtime.InvokerHelper.invokePojoMethod(InvokerHelper.java:766)
  • org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:754)
  • org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:170)
  • org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethod0(ScriptBytecodeAdapter.java:198)
  • com.nfjs.hls.blog.services.AuthenticationManagerImpl.authenticate(AuthenticationManagerImpl.groovy:34)

That StringType object was expecting a string, but got a org.codehaus.groovy.runtime.DefaultGroovyMethods$2. Seems like a little bit of Groovy has leaked unexpectedly into my Java.

The context is a method for authenticating a user's handle and password:

   Blogger authenticate(String handle, String plaintextPassword)
    {
        def authenticationCode = computeAuthenticationCode(handle, plaintextPassword) 

        def query = session.createQuery("from Blogger where handle = :handle and authenticationCode = :code")

        query.setProperties([handle: handle, code: authenticationCode])

        def result = query.uniqueResult()

        if (result)
            asm.get(UserCredentials.class).userId = result.id;

        return result
    }

   def computeAuthenticationCode(handle, plaintextPassword)
    {
        def md = MessageDigest.getInstance("MD5")

        // Salt the digest with the lower-cased handle
        md.update(handle.toLowerCase().bytes)
        md.update(plaintextPassword.bytes)

        md.digest().encodeBase64()
    }

A little hunting around showed that the ClassCastException was related to the authentication code.

Now the encodeBase64() method should be returning a String, one would think. Ah, a little poking around in the Groovy JDK docs and in fact, it returns Writable. After a few experiments, I found that the following change worked and was most pleasing:

   md.digest().encodeBase64() as String

This is the exact kind of error that static typing is designed to catch. Certainly, this would be caught by some tests eventually (I'm working a little fast and loose here), but in the end I had to work with the debugger to chase down what was wrong.

And that brings up a more significant problem: it appears that in Groovy, local variables are not visible in the debugger. I suspect they may be stuck inside the metaClass object or somewhere else I haven't found yet. That is significantly less than ideal. Update: User error; there was a button in the debugger pain that made the local variables visible.

What I'm finding, working with Groovy, is that for the trivial kind of code I'm writing to support my application, any extra bang I get for using Groovy instead of Java is being offset by leaky abstractions, my own lack of familiarity with Groovy, and the very shakey developer tools (even inside IDEA!). One aspect of this is just how trivial the Java code for a Tapestry application is; there's just not much room for Groovy to improve on pure and simple Tapestry Java.

However, the exercise is certainly worth it, because many people will be using Groovy with Tapestry and it should work without any hickups.

Tuesday, June 10, 2008

JavaScript var vs. Groovy def

I keep typing the following:

var blogger = new Blogger()
when I should be saying
def blogger = new Blogger()

def is used to define new methods and variables. The problem is that the JavaScript syntax compiles ... it just doesn't execute. I looks like a call to method var().

The code fails at runtime with groovy.lang.MissingPropertyException: "No such property: blogger for class: com.nfjs.hls.blog.pages.Login".

Just takes a little getting used to. Perhaps this is where polyglot programming breaks down a little ... switching between too similar languages.

Monday, June 09, 2008

Using Groovy for IoC Service Decorators

Continuing with my experiments with Groovy, I took a pass at writing a service decorator in Groovy. In Tapestry, the RequestHandler service is the primary execution path; everything filters through it. It's an extensible service, based on the pipeline pattern, so I could contribute a filter into the service, but instead I wrote a decorator around the service.

    def decorateRequestHandler(service, Logger logger)
    {
        {request, response ->
            def start = System.nanoTime()

            def result = service.service(request, response)

            def elapsed = System.nanoTime() - start

            logger.info(String.format("Request time: %5.3f s -- %s", elapsed * 10E-9d, request.path))

            return result
        } as RequestHandler
    }

Tapestry's naming convention is that a module class method named decorateXXX is a decorator for service XXX. The service implementation is passed in. The return value must be the same type as the service (that is, implement the service's interface). Again, we use a closure in Groovy as an easy alternative to an inner class in Java. The code here logs the elapsed time (in seconds) for the request.

What's interesting is just how much gets into the stack trace:

 at org.apache.tapestry5.internal.services.CheckForUpdatesFilter.service(CheckForUpdatesFilter.java:106)
 at $RequestHandler_11a6ead473a.service($RequestHandler_11a6ead473a.java)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:585)
 at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
 at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
 at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:912)
 at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:756)
 at org.codehaus.groovy.runtime.InvokerHelper.invokePojoMethod(InvokerHelper.java:766)
 at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:754)
 at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:170)
 at com.nfjs.hls.blog.services.AppModule$_decorateRequestHandler_closure2.doCall(AppModule.groovy:49)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:585)
 at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
 at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
 at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:248)
 at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:756)
 at groovy.lang.Closure.call(Closure.java:292)
 at org.codehaus.groovy.runtime.ConvertedClosure.invokeCustom(ConvertedClosure.java:48)
 at org.codehaus.groovy.runtime.ConversionHandler.invoke(ConversionHandler.java:72)
 at $Proxy147.service(Unknown Source)
 at $RequestHandler_11a6ead4732.service($RequestHandler_11a6ead4732.java)

In this view, $RequestHandler_11a6ead4732 is a Tapestry runtime fabricated class, the proxy for the service. $Proxy147 is a JDK dynamic proxy which is eventually hooked up to the closure code. The service parameter (to the decorateRequestHandler) is the instance of CheckForUpdatesFilter.

What looks like a simple method invocation turns into several levels of indirection through the Closure and MetaClass code, and then ultimately a reflective method invocation before it picks back up with the first filter in the pipeline (CheckForUpdatesFilter).

This is not especially troubling; I suspect it would be all but impossible to measure the "extra" time the Groovy code takes (vs. an equivalent Java implementation). Still, this could easily add up inside a tight loop, such as the rendering state machine built into Tapestry components. Certainly Merlyn was having performance issues with his visual processing application.

Sunday, June 08, 2008

Real World Haskell

When I'm in the mood to feel really dense, I try to wrap my brain around functional programming. My poison of choice is Haskell, since that's the purest and least compromising of the languages, in my uninformed opinion. I have a couple of books on the subject and every once and a while I struggle with the syntax and the alternate mode of thought.

I recently stumbled across a very approachable on-line book-in-progress about Haskell: Real World Haskell.

I still have my doubts about functional programming; I should love it, because I tend to think about code and how code fits together all the time. However, the heavy mathematical bent of functional programming is a bit of a challenge, and the FP-ers seem to value conciseness over all other things. I worry that Haskell programs are write-only in the way that Perl can be; that every program is an elegant puzzle-box, but a puzzle nonetheless.

I'm still intrigued not due to the conciseness, or the promise of parallel execution, but because of lazyness. Having lazy evaluation built right into the language means that a lot of common coding conventions and patterns are simply a natural byproduct of the use of the language.

For example, throughout Tapsestry there are examples of code that is based on a recursive algorithm implemented non-recursively (typically, using queues). In Haskell, you get that for free pretty much universally: the Haskell execution stack does not get very deep, though there's a kind of stack of as-yet-unevaluated expressions (it looks like the Haskell folk call these "thunks") behind the scenes, acting like the queues I'm coding directly.

Because it's baked into the language, you are free to express algorithms very purely, without the implementations being obscured by performance details (such as converting recursion into queues). In fact, the laziness of Haskell is really good at handling infinite lists (as long as you don't ask for the length of that list!)

There's a bit of hype going on in the FP/Haskell/Erlang space. For example, people keep talking a lot about the need for FP to tackle large numbers of cores. Does the Emperor have any clothes here? I'm not certain: Haskell and Erlang use non-native threads. That's a great way to maximize a single core and all the no-sideffects, no-mutable state simplifies the problem vastly from a typical Java application.

However, Erlang typically approaches the highly scalable space by starting (and, as necessary, restarting) lots of processes. The immutable state must be extracted from one process and serialized to another; Erlang has a native format for this that's pretty efficient. Haskell adds similar functionality as an add-on library. But from my point of view, it doesn't feel that different from JMS and messaging with a bit of coding discipline.

In fact, the point of view of 1060 NetKernel is that you can have the advantages of an Erlang style model, but implement it in your choice of languages (Java, JavaScript, Python, etc., etc.). We may have a gig at Formos where I'll have a chance to really look into NetKernel, I've been hovering around the edges on it for a couple of years.

Thursday, June 05, 2008

Time Breakdown of Modern Web Design

Tapestry with Groovy

While I'm waiting for the vote on Tapestry 5.0.12, I'm starting some work on a more complete demo application. As usual, I'm trying to teach myself a bunch of new things, such as using Ant and Ivy (in place of Maven), and using Groovy for my pages, entities and services.

I'm still having trouble with live class reloading in Tomcat, so I'm staying with Jetty for a bit longer.

The Groovy stuff for pages and such is just fine. My demo application is a simple blogging site (boring stuff, but a domain model everyone understands). I don't have much yet, but my first page lists recents blog postings:

<t:grid source="recentPostings"/>

And the page class is written in Groovy:

package com.nfjs.hls.blog.pages

import org.apache.tapestry5.ioc.annotations.Inject
import org.hibernate.Session

class Index
{
    @Inject
    Session session

    def getRecentPostings()
    {
        session.createQuery("from Posting order by posted desc").setMaxResults(20).list()        
    }
}

A few notes; the session gets injected even though the property is not private; Groovy is creating a private instance variable and a getter/setter for the property. That's just perfect.

I also like how succinct the getRecentPostings() method is. In previous apps, I tended to create a DAO service to hide some of the Java Generics ugliness, but that just stops being an issue here. With IDEA, I still get most of my editor support as well.

Tapestry has no knowledge of Groovy; IDEA is compiling the .groovy files to .class files and Tapestry is loading them. That's the beauty of the JVM.

For this simple example, we could write Java code that is nearly as succinct, but going forward I see no reasons why the Groovy code will not be significantly shorter, simpler and more readable than the equivalent Java code.

There's no reason to limit the use of Groovy to the pages and components. Entity classes in Groovy are also nice:

package com.nfjs.hls.blog.entities

import javax.persistence.*
import org.apache.tapestry5.beaneditor.DataType
import org.apache.tapestry5.beaneditor.NonVisual
import org.apache.tapestry5.beaneditor.ReorderProperties
import org.apache.tapestry5.beaneditor.Validate

@Entity
@ReorderProperties ("title,posted,content")
class Posting
{
    @Id
    @GeneratedValue (strategy = GenerationType.IDENTITY)
    @NonVisual
    Long id

    @ManyToOne (optional = false)
    Blog blog

    @Column (nullable = false)
    @Validate ("required")
    String title;

    @Column (nullable = false)
    @Validate ("required")
    @DataType ("longtext")
    String content

    @Column (nullable = false)
    Date posted
}

The @Validate annotations are Tapestry-specific and are being applied to the fields (allowing this is a new feature in 5.0.12). Because the getters and setters are generated without line numbers, the order of the properties ends up being a jumble; the @ReorderProperties annotation puts them into a reasonable order. You win some, you lose some.

On the services side of things, Groovy is a big win with Tapestry IOC. I have an AppModule to configure a few things, such as turning off production mode (which turns on full exception reports), and I want to override part of my Hibernate configuration in development mode:

package com.nfjs.hls.blog.services

import org.apache.tapestry5.SymbolConstants
import org.apache.tapestry5.hibernate.HibernateConfigurer
import org.apache.tapestry5.ioc.MappedConfiguration
import org.apache.tapestry5.ioc.OrderedConfiguration
import org.apache.tapestry5.ioc.annotations.Symbol

class AppModule
{

    def contributeApplicationDefaults(MappedConfiguration configuration)
    {
        configuration.add SymbolConstants.PRODUCTION_MODE, "false"
    }
  
    def contributeHibernateSessionSource(OrderedConfiguration configuration,

                                         @Symbol ("tapestry.production-mode")
                                         boolean productionMode)
    {
        if (!productionMode)
        {
            configuration.add "DevMode", {conf ->
                conf.setProperty "hibernate.show_sql", "true"
                conf.setProperty "hibernate.format_sql", "true"
                conf.setProperty "hibernate.hbm2ddl.auto", "create"
            } as HibernateConfigurer
        }
    }
}

The ability to wrap a closure as a simple interface (the kind of callback interface used throughout Tapestry) is very, very useful. The only fly in the ointment is GROOVY-2827, which keeps me from using SymbolConstants.PRODUCTION_MODE with the @Symbol annotation on the productionMode parameter.

I think I can get used to the Groovy syntax; I actually prefer the Groovy closure syntax to the Ruby block syntax though I generally prefer the Ruby approach elsewhere.

I expect to write all of this application in Groovy, and that may ultimately become an overall recommendation. Just us crazy framework authors should be coding in pure Java!

Friday, May 30, 2008

IntelliJ Diana Growls

That was a surprise: I downloaded IntelliJ 8 preview (Diana) and after it compiles, it uses Growl (the centralized Mac notification app) to inform me that its completed. Nice. Screenshot

I'm also experimenting with using the Mac OS X look and feel (rather than Alloy). I had used Alloy in the past for performance reasons but IntelliJ 8 feels a bit faster. In addition, it's nice having the menu bar be where it's supposed to be ... the Mac look and feel puts it on the Mac menu bar, but Alloy puts it, Windows-style, in each top-level frame.

Thursday, May 29, 2008

Nearly ready for 5.0.12

Tapestry 5.0.12 will be available pretty soon; I'm working on one very important component that's tricky to get Just Right: AjaxFormLoop. This is a component that allows you to dynamically add and remove rows (or divs, list items, whatever) that represent detail objects under a master object ... think line items in an invoice. The trick, of course, it to make it useful as-is, and to make it easy to customize it. It takes a bit of state-management gymnastics to make everything work just right, but it's almost there.

I expect to get this finished up in the next couple of days along with other work, and create a 5.0.12 release early next week.

Thursday, May 15, 2008

Tapestry 5 with NetBeans

At NFJS Boston last month, I ran into Alex Kotchnev. We had a number of chats about Tapestry and spurring wide adoption. I'm still working on some of those ideas. He's a NetBeans user whereas most of the documentation assumes Eclipse or IDEA. He's posted a blog about use Tapestry in NetBeans; specifically, using the Maven support to avoid typing the dreaded Maven project creation incantation.

Monday, May 12, 2008

Tapestry for Nonbelievers at InfoQ

Renat and Igor's long awaited article, Tapestry for Nonbelievers is finally available. Check it out!

My only real criticism is that, had they focused on Hibernate, some of the code that switches back and forth between entities and entity ids would be handled automatically by tapestry-hibernate.

They're promising to follow through with more articles. I welcome it!

Wednesday, May 07, 2008

Improvements to the Tapestry 5 Tutorial

I spent some time yesterday revamping the Tapestry 5 Tutorial; you can see the updates at the nightly build site.

In short order we turned the Address object into a Hibernate entity, and stored it in a MySQL database, then used a Grid to show the added Addresses. Later improvements to the Tutorial will show editting and removal of Addresses.

I think the correct reaction to this would be "Dude, where's my code?". The application code for this app is so small you'd think something was missing. But that's what's Tapestry is all about ... providing the structure so that the framework can do the busy work.

I also updated some of the existing screen shots to show the "error bubbles" style of client-side validation that's been in T5 for quite some time.

People have been pining for a 5.0.12 release, but I'm glad I've been holding it back; I've been having a chance to fix bugs, and finding many annoyances as I work on a client project. Working in earnest on a project is always the best way to find the rough edges, and the end result is much more polished.

I think I must be a harsher critic of Tapestry than most; I frequently add bugs along the lines of "when I screw up, Tapestry should tell me how to fix it".

Friday, April 25, 2008

Tapestry: Components + Aspects

When I was first starting on the Tapestry 5 code base, I started using AspectJ for parts of the framework. I wrote some clever aspects for defensive programming (cementing "don't accept null, don't return null" as an iron-clad rule) and created some aspects to simplify concurrent access to certain resources.

I certainly never expected Tapestry developers to have to use AspectJ: it's certainly a powerful tool, but it has downsides: AspectJ affects the build process in a signifcant way, it's tricky to use properly, it adds a huge runtime dependency and the plugin for Eclipse made the IDE slow(-er) and (more) unstable. I eventually stripped out the AspectJ code entirely and restricted the framework to traditional code (plus, of course, Javassist).

In the interrum a number of aspect oriented touches have appeared in Tapestry. Certainly, the entire mixins concept is an approach to aspect orientation. Likewise, service decorators. In fact, much of the class transformation mechanism is a way of performing aspect-oriented operations on Tapestry component classes. Creating your own parameter binding prefixes (something I often do for client projects) is another aspect: one focused on streamlining how data gets into or out of a component.

However, all of these techniques can be cumbersome; they are built on the Javassist "language" which is another tool that can be difficult to use properly. I've created layers around Javassist to make things easier, but it's still an uphill battle.

Earlier in the week I was able to take the Javassist out of service method advice. However, that's not quite enough ... I want it to be easier to apply advice to component methods just as easily.

Well, I did just that. There is a ComponentMethodAdvice that can be applied to component methods. It's only a slight variation from the MethodAdvice used on service interfaces (the difference being that the invocation allows access to the ComponentResources of the component).

Here's a concrete example: Part of tapestry-hibernate is the @CommitAfter annotation, which is used to commit the running transaction after invoking a method. There's a decorator that can be applied to services, and there's a worker that is automatically threaded into the Component Class Transformation chain of command.

The worker used to use the Javassist approach to decorate method invocations with the desired logic: commit the transaction when the method completes normally, or with a checked exception. Abort the transaction when the method throws a runtime exception.

First, the old code:

public class CommitAfterWorker implements ComponentClassTransformWorker
{
    private final HibernateSessionManager _manager;

    public CommitAfterWorker(HibernateSessionManager manager)
    {
        _manager = manager;
    }

    public void transform(ClassTransformation transformation, MutableComponentModel model)
    {
        for (TransformMethodSignature sig : transformation.findMethodsWithAnnotation(CommitAfter.class))
        {
            addCommitAbortLogic(sig, transformation);
        }
    }

    private void addCommitAbortLogic(TransformMethodSignature method, ClassTransformation transformation)
    {
        String managerField = transformation.addInjectedField(HibernateSessionManager.class, "manager", _manager);

        // Handle the normal case, a succesful method invocation.

        transformation.extendExistingMethod(method, String.format("%s.commit();", managerField));

        // Now, abort on any RuntimeException

        BodyBuilder builder = new BodyBuilder().begin().addln("%s.abort();", managerField);

        builder.addln("throw $e;").end();

        transformation.addCatch(method, RuntimeException.class.getName(), builder.toString());

        // Now, a commit for each thrown exception

        builder.clear();
        builder.begin().addln("%s.commit();", managerField).addln("throw $e;").end();

        String body = builder.toString();

        for (String name : method.getExceptionTypes())
        {
            transformation.addCatch(method, name, body);
        }
    }
}

Can you tell where that logic (when to commit and abort) is? Didn't think so, it's obscured by lots of nuts-and-bolt logic for creating the Javassist method body.

All that stuff with the BodyBuilder is a way to assemble the Javascript method body piecewise. It's certainly meta-programming, but it's only a few steps ahead of writing the Java code to a temporary file and compiling it. That's great for people into the meta-coding concept (count me in) but sets the bar so high that most people will never be able to tap into the power.

That's where the advice comes in: the new version of the worker is much simpler:

public class CommitAfterWorker implements ComponentClassTransformWorker
{
    private final HibernateSessionManager _manager;

    private final ComponentMethodAdvice _advice = new ComponentMethodAdvice()
    {
        public void advise(ComponentMethodInvocation invocation)
        {
            try
            {
                invocation.proceed();

                // Success or checked exception:

                _manager.commit();
            }
            catch (RuntimeException ex)
            {
                _manager.abort();

                throw ex;
            }
        }
    };

    public CommitAfterWorker(HibernateSessionManager manager)
    {
        _manager = manager;
    }

    public void transform(ClassTransformation transformation, MutableComponentModel model)
    {
        for (TransformMethodSignature sig : transformation.findMethodsWithAnnotation(CommitAfter.class))
        {
            transformation.advise(sig, _advice);
        }
    }
}

Really simple! The advice is very clear on what happens when, as long as you realize that proceed() invokes the original method (the method "being advised"), and also Tapestry's way of handling thrown exceptions: runtime exceptions are not caught by proceed(), but checked exceptions are.

This simplicity opens up the power of aspects without all mental overhead of AspectJ's join points and declarative point-cut language. It's only a fraction of AspectJ's power, but it's a very useful fraction.

Further, AspectJ works entirely in a static way (it primarily works at build time), but this style of AOP is a mix of declarative and imperative. In other words, we can have active code decide on a component-by-component and method-by-method basis what methods should be advised. The above example is typical: it's driven by an annotation on the method ... but other cases jump to mind: triggered by naming conventions, by parameter or return types, or by thrown exceptions. And there's no guesswork about going from the advice to Tapestry services: its the same uniform style of injection used throughout Tapestry.

In the future, we may add other options, such as advice for field access, but what's just checked in is already a huge start.

Sunday, April 20, 2008

Tapestry AOP: MethodAdvice

I don't go out of my way to talk about Tapestry 5 IoC, since it is really just a side issue compared to the main framework (but it is the infrastructure that allows Tapestry to be everything it is).

However, the IoC container is quite powerful and fully featured and quite useful on its own. However, one feature of the container wasn't quite up to snuff: service decorators. Service decorators are a limited form of aspect oriented programming wherein its possible to "wrap" a service with an interceptor that provides additional behavior. Tapestry includes two decorators: one for adding logging of method invocations, results and exceptions and a second decorator for managing Hibernate transactions.

Tapestry is responsible for invoking a decorator method, but the responsibility of that method was to return an interceptor object that encapsulates the behavior. So, a logging interceptor is responsible for logging method entry and exits around re-invoking each method on the service, and a transaction interceptor is responsible for commiting or aborting the transaction after invoking the service method.

The problem is how to provide such an interceptor. Option A is to use a JDK dynamic proxy. This is a perfectly good solution, but has a few annoyances; you drop out of pure bytecode which means that Hotspot will not be able to fully optimize your code. In addition, there a minute cost associated with constantly boxing and unboxing primitives and packaging parameters up as an object array.

Option B is to use Tapestry's ClassFactory and ClassFab interfaces to create a new class and then instantiate. Javassist is under the surface, but the APIs are tamed. Even so, its a lot of work and a lot to test to check all the edge cases such as handling of primitives (more boxing and unboxing). This was the approach I've been taking in the past.

So I've taken some inspiration from the AOP Alliance approach.

You can now create an interceptor using a special service, AspectDecorator, and providing MethodAdvice. For example, the HibernateTransactionDecorator (which looks for the @CommitAfter annotation) is simplified to:

public class HibernateTransactionDecoratorImpl implements HibernateTransactionDecorator
{
    private final AspectDecorator _aspectDecorator;

    private final HibernateSessionManager _manager;

    private final MethodAdvice _advice = new MethodAdvice()
    {
        public void advise(Invocation invocation)
        {
            try
            {
                invocation.proceed();
            }
            catch (RuntimeException ex)
            {
                _manager.abort();

                throw ex;
            }

            // For success or checked exception, commit the transaction.

            _manager.commit();
        }
    };

    public HibernateTransactionDecoratorImpl(AspectDecorator aspectDecorator, HibernateSessionManager manager)
    {
        _aspectDecorator = aspectDecorator;

        _manager = manager;
    }

    public <T> T build(Class<T> serviceInterface, T delegate, String serviceId)
    {
        String description = String.format("<Hibernate Transaction interceptor for %s(%s)>",
                                           serviceId,
                                           serviceInterface.getName());

        AspectInterceptorBuilder<T> builder = _aspectDecorator.createBuilder(serviceInterface, delegate, description);

        for (Method m : serviceInterface.getMethods())
        {
            if (m.getAnnotation(CommitAfter.class) != null)
            {
                builder.adviseMethod(m, _advice);
            }
        }

        return builder.build();
    }
}

At this weekend's No Fluff Just Stuff, Neal Ford went out of his way to point out how hard meta-programming in Java is, and how impressed he was by what I've accomplished using meta-programming throughout Tapestry. Even so, he and the other speakers voted me "most in need of a dynamic programming language" behind my back!

Meanwhile, this MethodAdvice approach (the terminology is borrowed from AOP and AspectJ) really is a very succinct way of expressing this transaction concern, and it's so easy to mix and match methods within the interface, advising some of them and letting the others just "pass thru". There's going to be a lot of room to use this same technique to provide many other cross-cutting concerns such as performance monitoring, security checks ... even checking for null pointers!

Thursday, April 17, 2008

Service Dependencies in Tapestry 5

I was curious to see if I could generate a diagram that showed how all the services inside Tapestry 5 are interconnected. In the diagram, solid lines are dependencies, dashed lines are contributions, and dotted lines represent a services that listens to events from another service.

The final diagram is a bit complex. Ok, that's a tremendous understatement. Yellow nodes are public services, grey nodes are internal services, and orange nodes are simple beans (contributed into services) ... those beans, though, may still have their own dependencies. I think OmniGraffle choked on the complexity (why do some of the lines zip around so aimlessly?) and kind of "phoned it in" in terms of layout. I had hoped for something that could go on a T-shirt.

Worse yet, this is only a partial diagram; I didn't cover many of the dependencies that arrive as contributions to services (there's just a few of those in there). Those contributions would show up as even more orange nodes; these additional nodes would provide connections to many of the outliers that appear to be unreferenced.

Is this good or bad? Is it too complex? Can it be simplified? Does all that really need to be in there?

I think it is good: When I'm coding, I find it easy enough to work from a service interface, to the implementation of the interface, to the dependencies of the implementation. This works for me when developing Tapestry itself, or writing apps in Tapestry. Since there are so many small objects, no single object is large, or complex, or hard to understand or debug. It's a bit daunting to know where to start, I'm sure. However, only a few of these services are all that relevant for day-to-day development of Tapestry.

The moral of the story is that simplicity is hard! In Tapestry, pages and templates are as simple and straight forward as I think is possible. There's no excess, no distractions, no clutter, no boilerplate. Much of the logic represented in the diagram is to "fill around the edges" of this simplicity, to bridge the gap from the simple Tapestry component model to the much more complex world of stateless HTTP and multi-threaded request handling.

New Tapestry 5 Features

I've been using Tapestry 5 for a client project, which is a great chance to find the rough edges. If you've been following the bug list, you'll have seen lots of minor bug fixes, many related to JavaScript.

I've also added a couple of powerful new features.

The Grid component now has an inplace parameter; setting this to true "Ajax-ifies" the Grid; it updates in place. Clicking any of the paging or sorting controls will repaint the Grid in place, without affecting the rest of the page. Handy. I had originally though in terms of creating an AjaxGrid component for this purpose capable of redrawing individual cells ... now I'm not so sure that's necessary.

Perhaps more importantly, I've added a new annotation, @CommitAfter, that can be placed on component methods, to indicate the Hibernate transaction should be committed after invoking the method. It can also be used with services, via a decorator. In any case, much of the work was contributed by Igor Drobiazko.

These features are available in the nightly build, and will be part of Tapestry 5.0.12.

They are also a good indication of how Tapestry future backwards compatibility will work. The new behaviors "slots in" with the existing behavior. In this case, a new annotation triggers new behavior, or a new parameter is added to an existing component. These are the vectors by which Tapestry adapts to your code (and not vice-versa).

In the future, we'll be adding much more dramatic new features, as well as integrations with other frameworks, such as Seam, Spring Web Flow, Lucene, Quartz and other parts of the evolving standard open source stack. But due to the baked in elegance of Tapestry, they will integrate with barely a ripple.

Tuesday, April 15, 2008

A round-up of Tapestry blogs

I was just fiddling with Google's Blog Search and searched for "java tapestry"; here's a few things I stumbled upon:

Fun stuff ... and I have more advancements for Tapestry 5 about ready to announce!

Sunday, March 30, 2008

Web Framework Smackdown: Tapestry

I wasn't able to attend this years TSSJS and I really would have liked to be in on Matt Raible's web framework smackdown. Matt has raised a number of questions, here are the responses for Tapestry 5:

  • What is the overall performance of your framework as it compares to others?

    I haven't done any direct performance comparisons, but Tapestry 5 is considerably faster than Tapestry 4, which was already "fast enough". Using FireBug, I see that most requests (on my development machine) are on the order of 4 - 6 ms to process a request and get the response stream back; it takes the browser much longer to render the HTML up onto the screen. Tapestry uses no runtime reflection and doesn't appear to have any concurrency bottlenecks (the way OGNL can be in Tapestry 4).

  • How does your web framework position themselves in relation to Web Beans?

    Web Beans is one way of looking at how to manage state and process in a web application, but not the only one. Tapestry is already being used with Seam, and integration with Spring Web Flow will come some day (likely 5.1 as we're trying to get a stable 5.0 release out the door). Tapestry is in a good position to "embrace and extend" these kinds of specifications, since it doesn't care about anything but JavaBeans properties.

  • How easy is it to create a re-useable component in your framework? Is it as easy as sub-classing an existing component?

    You can subclass an existing component, but you can create a component as just a POJO with no inheritance. There's no configuration (other than putting it in the correct components package for your application). Bi-directional component parameters are as easy as placing a @Parameter annotation on a field. Components may have templates and may contain other components, it's all very consistent. It's always been easy to create components in Tapestry, but Tapestry 5 adds a number of features that make it even easier (such as no longer requiring you to select the correct base class). Further, it is much easier in Tapestry 5 to subclass a component and inherit its template and message catalog, but add new functionality. In addition, Tapestry 5 has the concept of Mixins, which provide another way to add new functionality to an existing component.

  • What is the key differentiating characteristic of your framework that makes it better than the rest?

    A ruthless emphasis on developer productivity drives nearly all the design decisions in Tapestry: this shows up with live class reloading, exceptional error reporting, adaptive APIs, and minimizing the amount of code (and markup) in an application. In terms of implementation, Tapestry embraces pervasive object orientation (not just inheritance), meaning lots of small focused objects able to work together seamlessly.

  • What do you think about the various scopes introduced by Seam, e.g. conversation vs request or session? If you support these additional scopes, do you also provide some sort of concurrency control?

    These are very important; Tapestry already has a number of ways of managing server-side state; individual page or component fields may be stored on the client, rather than in the session, for example. As with much of T5, the support is cleanly extensible, so natively supporting conversational state without using Seam is a possibility, as is leverage Seam itself (there's an add-on library for this), as are many other options.

  • Why can't we, the Java Community, come together and adopt the best application framework and settle the web development subject?

    That's like saying, "Why can't we, the developer community, come together and adopt the best language?". Generally, people with a lot of experience in one framework are looking for a bigger, badder version of their framework, that also magically solves all their problems ... especially those caused by their choice of framework. Sometimes it's the craptaculous framework. Often the fix is something is a completely different vein (and Tapestry has always been that different vein). It's kind of like the adage about a council of Great Apes designing their evolutionary successor. They'd probably come up with a bigger, stronger Ape ... not a scrawny, weak, hairless human.

  • What are you doing to help with developer productivity?

    Tapestry is primarily focused on developer productivity, and this should not be confused with simply adding tool support (and dealing with wizard-generated code). Tapestry addresses the needs of developers to build applications using simple, off the shelf tools, such as Java IDEs and standard XML editors. Tapestry has best-of-breed exception reporting. This is more than a fancy exception report page; it's also attention to detail throughout the framework to catch problems and report them in a useful manner to the developer. For example, if you use the wrong name when defining a component or referencing a page, Tapestry doesn't just throw a NullPointerException the way too many libraries would; it clearly identifies the line in your template with the problem, identifies the name you gave, and provides a list of possible values to help you make the correction. Oh, and it's generally case insensitive. Add live class reloading on top of this, and you can burn through your application very quickly. In fact, Tapestry addresses developer productivity by making the development more Fun and less of a Drudge.

  • 2008 is a huge year for the mobile web. How do you help developers build great mobile web applications?

    Size counts on the mobile web and Tapestry has been built with terseness as a virtue. URLs are shorter and more readable, page content is simpler, and many components include a "lean" mode to reduce optional content generation (such as CSS classes inside the Grid component).

  • If you couldn't use your framework, what would you use and why?

    I'd be very interested in learning to really leverage Adobe's Flex ... if I can't use Tapestry, I want something that gives me pixel-level control and good abstraction without the kludges and half measures of JavaScript and the DOM.

  • How do you enable rich Ajax applications?

    Tapestry supports very clean interactions between the client and the server, as server-side components can easily be referenced via a URL. Handling Ajax requests in Tapestry is virtually identical to handling of traditional page-oriented requests. A limited amount of base support is provided in the framework (text field autocompletion, dynamic content zones, extensible forms) with much more support provided by the community. Currently Ajax support is a wrapper on top of Prototype/Scriptaculous and there is great desire to make the client-side support more agnostic.

  • Can a developer make a change to source, and hit RELOAD in the browser to see the change? If not, why not?

    Absolutely. I think Tapestry was on the cutting edge nearly two years ago when this feature was introduced in early versions of Tapestry 5; other frameworks are still trying to catch up. This feature is critical to developer productivity and making it fun. Further, Tapestry's approach to server-side state means that persistent data is not lost on reload, which significantly enhances the developer's ability to continue work from where they left off. Tapestry is even pretty good about picking up the addition of new pages and components, not just updates to existing Java classes.

  • What do you think about the whole Flex revolution, and do you think you are competitors to this technology?

    Flex is an impressive piece of work, and it gives you quite a lot of power. Still, I think RIAs (such as Flex) are in a parallel, not competing, category against traditional web applications (even those enhanced with Ajax) . Tapestry has an emphasis on bookmarkable URLs, and though Flex supports this feature, there are some costs and limitations. I don't want to spread FUD, but to me a Flex application is pretty heavy weight ... big enough to require a loading screen, whereas a web application just pops up instantly. I haven't pushed myself far enough up the Flex learning curve to talk in more detail. I tend to discount the "Grandma factor": that is, Flash is almost certainly already installed on Grandma's computer. Even so, Flex's greatest strength and greatest weakness is the proprietary Flash runtime: even more than Sun, Adobe is protective of their runtime environment, and fearful of splintering. This is increasingly unsettling to developers used to full open source everything.

    P.S. I'm sure James Ward will correct everything I've just stated.

  • How easy is it to create a module and plug it into a bigger application, complete with configuration, code, and view?

    Really, really easy. Choose a package name, put your pages, components, base classes, etc. in sub-packages, along with any templates. Create a module class for your library to provide Tapestry with configuration for the module's package. One tweak to your Ant or Maven build script to enable autoloading of the module, at which point it Just Works. Tapestry takes care of all sorts of issues, including providing secure and efficient client-side access to assets (images, stylesheets, etc.) packaged inside your library's JAR.

Friday, March 14, 2008

Tapestry 5.0.11

Tapestry 5.0.11 is now available for download. This is unofficially a beta, meaning that the functionality is really nailed down and we're just stabilizing and fixing bugs.

There's a lot of cool new things in this release. I think the three biggest are:

  • HTTPS control: annotate a page as only accessible via HTTPS. Tapestry takes care of the rest.
  • Index pages for folders: kind of like the Start page, but everywhere.
  • Automatic Hibernate ValueEncoders: Use an entity as a page or event context and what goes in the URL is the entity's id. Likewise, ids are turned back into entities when passed to event handler methods.

What's next? A period of stabilization and bug fixes and then a release candidate. And then, some really awesome (and really backwards compatible) stuff for the 5.1 release.

Thursday, February 28, 2008

No Fluff Just Stuff / Danvers, MA

I'll be kicking off my participation in the No Fluff Just Stuff symposium series this year on Sunday April 6th. I'll be talking about Tapestry, testing and design patterns.

I'm especially looking forward to the Tapestry session, of course, as I have so much more to say on Tapestry this year than last. But I also enjoy the testing talks (it's a pair of talks), perhaps because I'm not as emotionally invested in EasyMock and TestNG as I am in Tapestry.

Monday, February 25, 2008

Tapestry 5 Index Pages

I've been rapidly closing the gap between where Tapestry 5 is and where it needs to be for a stable release.

Today I added in index pages; literally, pages named Index.

In the past, you could create a Start page and it would be used for any request that just specified the servlet context but didn't identify a specific page. That was great ... at the top level. It's also supported for backwards compatibility (even I am not that cruel).

However, the preferred method is to call that page Index. Further, you can place Index pages in subfolders as well. Tapestry can reference such a page using just the folder name.

That's great so far, but nobody wants to have a bunch of classes with the same name, even in different folders. No problem; the standard "name stripping" occurs. Thus, you might have an ecommerce application and three pages : com.myco.pages.order.OrderIndex, com.myco.pages.order.OrderVerify and com.myco.pages.order.OrderSubmit. The names of these three pages will be "order/Index", "order/Submit" and "order/Verify" (the redundant parts are stripped out). Lastly, Tapestry will create URLs of the form ".../context/order", ".../context/order/submit" and ".../context/order/verify".

In other words, short, sensible URLs and no compromises in your code. And, as always, any extra path info beyond the page name is treated as page activation context, made available to the page via the "activate" event handler method.

I'm very happy with this change; I also think Tapestry's built-in rules for URLs are really strong and appealing; I simply don't see customizing the built-in rules to be a priority, or frankly, of any great value.

Thursday, February 21, 2008

Secure applications in Tapestry 5

Tapestry 5 features continue to come together rapidly; the latest is HTTPS support.

It's very simple: mark a page as secure ...

@Secure
public class ProcessOrder
{
  . . .
}

... and Tapestry takes it from there. Every request for that page will automatially use an HTTPS protocol URL. Any attempt to circumvent HTTPS will result in an HTTPS redirect; there's no way to access the page without HTTPS. Links from a secure page to an insecure page use the standard "http" protocol ... so Tapestry handles the whole cycle, from insecure to secure and back again.

That's about it; behind a firewall there's a little bit more configuration to allow Tapestry to know how to properly build the complete URL (the URL with protocol, server name and maybe port) since (behind a firewall) the servlet API doesn't properly report the information the way the client web browser needs it.