Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin

Wiki Markup
{scrollbar}

Service Configurations

This is an area of Tapestry IoC that is often least well understood. Tapestry services often must have some configuration to fine tune exactly what they do. One of the interactions between modules is that these service configurations are shared: they may be contributed into by any module.

...

The Tapestry module makes a contribution into the service configuration:

Code Block
borderStylesolid
  public static void contributeResourceDigestGenerator(Configuration<String> configuration)
  {
    configuration.add("class");
    configuration.add("tml");
  }

This is a service contribution method, a method that is invoked to provide values for a configuration. We'll see how the service receives these contributions shortly. The Configuration object is how values are added to the service's configuration. Other parameters to a service configuration method are injected much as with a service's constructor, or a service builder method.

...

Say your application stored a file on the classpath needed by your application; for illustrative purposes, perhaps it is a PGP private key. You don't want any client to able to download a .pgp file, no matter how unlikely that would be. Thus:

Code Block
borderStylesolid
public class MyAppModule
{
 public static void contributeResourceDigestGenerator(Configuration<String> configuration)
 {
   configuration.add("pgp");
 }
}

The contribution in MyAppModule doesn't replace the normal contribution, it is combined. The end result is that .class, .tml and .pgp files would all be protected.

...

A service receives the configuration as an injected parameter ... not of type Configuration (that's used for making contributions), but instead is of type Collection:

Code Block
borderStylesolid
public class ResourceDigestGeneratorImpl implements ResourceDigestGenerator
{
  private final Set<String> digestExtensions;

  public ResourceDigestGeneratorImpl(Collection<String> configuration)
  {
      digestExtensions = new HashSet<String>(configuration);
  }

  . . .
}

In many cases, the configuration is simply stored into an instance variable; in this example, the value is transformed from a Collection to a Set.

These kinds of unordered configurations are surprisingly rare in Tapestry (the only other notable one is for the TypeCoercer service). However, as you can see, setting up such a configuration is quite easy.

...

Alternately, if the Request can't be handled, the Dispatcher returns false.

Code Block
borderStylesolid
public void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration, . . .)
{
  // Looks for the root path and renders the start page

  configuration.add("RootPath", new RootPathDispatcher(. . .), "before:Asset");

  // This goes first because an asset to be streamed may have an file extension, such as
  // ".html", that will confuse the later dispatchers.

  configuration.add(
          "Asset",
          new AssetDispatcher(. . .),
          "before:PageRender");

  configuration.add("PageRender", new PageRenderDispatcher(. . .));

  configuration.add("ComponentAction", new ComponentActionDispatcher(. . .), "after:PageRender");
}

With an OrderedConfiguration, each contribution gets a name, which must be unique. Here the names are RootPath, Asset, PageRender and ComponentAction.

...

The MasterDispatcher service configuration defines a Chain of Command and we can provide the implementation using virtually no code:

Code Block
borderStylesolid
  public static Dispatcher buildMasterDispatcher(List<Dispatcher> configuration, ChainBuilder chainBuilder)
  {
    return chainBuilder.build(Dispatcher.class, configuration);
  }

ChainBuilder is a service that builds other services. Here it creates an object of type Dispatcher in terms of the list of Dispatchers. This is one of the most common uses of service builder methods ... for when the service implementation doesn't exist, but can be constructed at runtime.

...

The first step is to contribute values.

Code Block
borderStylesolid
  public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration)
  {
    configuration.add(SymbolConstants.FILE_CHECK_INTERVAL, "1000"); // 1 second
    configuration.add(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT, "50"); // 50 milliseconds
    configuration.add(SymbolConstants.SUPPORTED_LOCALES, "en");
    configuration.add("tapestry.default-cookie-max-age", "604800"); // One week
    configuration.add("tapestry.start-page-name", "start");
    configuration.add("tapestry.scriptaculous", "classpath:${tapestry.scriptaculous.path}");
    configuration.add(
            "tapestry.scriptaculous.path",
            "org/apache/tapestry5/scriptaculous_1_7_1_beta_3");
    configuration.add("tapestry.jscalendar.path", "org/apache/tapestry5/jscalendar-1.0");
    configuration.add("tapestry.jscalendar", "classpath:${tapestry.jscalendar.path}");
  }

These contribution set up a number of defaults used to configure various Tapestry services. As you can see, you can even define symbol values in terms of other symbol values.

Mapped configurations don't have to be keyed on Strings (enums or Class are other common key types). When a mapped configuration is keyed on String, then a case-insensitive map is used.

Wiki Markup
{scrollbar}