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.

Let's start with the most basic kind, the unordered configuration.

Unordered Service Configurations

One of Tapestry's features is the ability to package assets (images, style sheets, javascript JavaScript libraries, etc.) inside JAR files and expose those to the client. For example, an application URL /assets/org/example/mylib/mylib.js would refer to a file, myllib.js, stored on the classpath in the /org/example/mylib folder.

...

This is controlled by the ResourceDigestGenerator service, which uses its configuration to determine which file extensions require an MD5 digest.

Contributing to a Service

Main Article: Tapestry IoC Configuration

...

Code Block
borderStylesolid

  public static void contributeResourceDigestGenerator(Configuration&lt;String&gt;Configuration<String> configuration)
  {
    configuration.add("class");
    configuration.add("tml");
  }

...

Code Block
borderStylesolid

public class MyAppModule
{
 public static void contributeResourceDigestGenerator(Configuration&lt;String&gt;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.

Receiving the Configuration

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&lt;String&gt;Set<String> digestExtensions;

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

  . . .
}

...

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.

Ordered Configurations

Ordered configurations are very similar to unordered configurations ... the difference is that the configuration is provided to the service as a parameter of type List. This is used when the order of operations counts. Often these configurations are related to a design pattern such as Chain of Command or Pipeline.

Here, the example is the Dispatcher interface; a Dispatcher inside Tapestry is roughly equivalent to a servlet, though a touch more active. It is passed a Request and decides if the URL for the Request is something it can handle; if so it will process the request, send a response, and return true.

...

Code Block
borderStylesolid

public void contributeMasterDispatcher(OrderedConfiguration&lt;Dispatcher&gt;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");
}

...

Here, we need a specific order, used to make sure that the Dispatchers don't get confused about which URLs are appropriate ... for example, an asset URL might be /assets/tapestry5/tapestry.js. This looks just like a component action URL (for page "assets/tapestry5/tapestry" and component "js"). Given that software is totally lacking in basic common-sense, we instead use careful ordering of the Dispatchers to ensure that AssetDispatcher is checked before the ComponentAction dispatcher.

Receiving the Configuration

The configuration, once assembled and ordered, is provided as a List.

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&lt;Dispatcher&gt;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.

Mapped Configurations

The last type of service configuration is the mapped service configuration. Here we relate a key, often a string, to some value. The contributions are ultimately combined to form a Map.

Tapestry IoC's symbol mechanism allows configuration values to be defined and perhaps overridden, then provided to services via injection, using the @Value annotation.

The first step is to contribute values.

Code Block
borderStylesolid

  public static void contributeFactoryDefaults(MappedConfiguration&lt;StringMappedConfiguration<String, String&gt;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}");
  }

...

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}