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!

Wednesday, June 09, 2004

New in HiveMind: PipelineService

Just finished checking in the hivemind.lib.PipelineFactory service into HiveMind.

This is neat stuff; pipelines are a close cousin to interceptors, but tend to be things that are a) coded manually and b) specific to a particular service. Think Servlets and Servlet Filters.

The PipelineFactory is a service implementation factory that constructs a pipeline ... a series of filters that call each other, then eventually call an underlying service. It looks like this at runtime:

The bridge classes are fabricated at runtime and connected together with the filters. Each filter is passed the next bridge as a parameter (the last filter gets the terminator instead), and is free to invoke the bridge before or after doing some kind of work, and to change the parameters as it sees fit.

A minimal use of this shows up as a service, a configuration, and some contributions to the configuration:

service-point (id=MyPipeline interface=mypackage.MyService)
{
  invoke-factory (service-id=hivemind.lib.PipelineFactory)
  {
    create-pipeline (filter-interface=mypackage.MyFilter configuration-id=MyPipeline)
  }
}

configuration-point (id=MyPipeline schema-id=hivemind.lib.Pipeline)

contribution (configuration-id=MyPipeline)
{
  filter (service-id=FrobFilter)
  filter (service-id=BazFilter)
  filter (service-id=FooFilter after="*")
}

This will be very useful in Tapestry 3.1 because many different services (not just Tapestry engine services, but all kinds of infrastructure) will be implemented as pipelines of simpler services. These pipelines will be exposed as configurations that will be pluggable. Got a particular transaction management strategy that you want to apply to all requests? Plug into the correct pipeline. Want to add some authorization checks to some kinds of requests? Plug into those service's pipelines.

This concept is demonstrating two key selling points of HiveMind:

  • Libraries can provide configuration points that most applications can ignore, but certain applications can plug into.
  • Mixing services and configurations (services passed as data, or services defined in terms of contributed data) extends the power of post the service model and the configuration model.

Once again, this underscores the difference in philosophies between HiveMind and Spring ... in Spring you still code against the metal. It's a much better metal to code against than the raw Java APIs or the raw J2EE APIs, but to make use of a service provided by Spring, you need to know a lot of details about how and when to configure and instantiate it ... it all goes into your springbeans.xml file.

HiveMind's philosophy is that just by having a framework available on the classpath, it will provide services and configurations that you don't need to know about. You can find out about them via HiveDoc (and other documentation) and take advantage of them as needed.

Take the example above: you don't need to know about the implementation of the PipelineFactory:

service-point (id=PipelineFactory interface=org.apache.hivemind.ServiceImplementationFactory)
{
  invoke-factory (service-id=hivemind.BuilderFactory)
  {
    construct (class=org.apache.hivemind.lib.pipeline.PipelineFactory service-id-property=serviceId)
    {
      set-service (property=classFactory service-id=hivemind.ClassFactory)
      set-service (property=defaultImplementationBuilder service-id=DefaultImplementationBuilder)
    }
  }
}

This configuration, and its dependencies on the ClassFactory and DefaultImplementationBuilder services, are the PipelineFactory's concern. It might change at some point due to some refactoring within the HiveMind framework ... and it won't affect your code at all. You may use some hypothetical third party framework and it may make use of PipelineFactory in its module descriptor and you don't have to know about it.

This is not all to say you couldn't create some kind of pipeline factory in Spring. You absolutely could ... but its a lot of work. It would be easier to create a bunch of individual beans and configure them to each other ... you don't save a lot of effort by adding additional abstractions (though you'd probably have to code a "bridge" class manually) ... you don't save anything because you are responsible for defining and configuring every bean in your application, even when the code comes out of a third party framework.

Creating a pipeline like this in HiveMind is a bit of effort, a new recipe to follow. However, contributing into your own pipeline, or a pipeline provided by another framework, is very streamlined. The way HiveMind can mix and match configurations and services, and the way it can accumulate contributions from multiple modules, is the key to this ease of use.

2 comments:

Unknown said...

I'll have some good examples coming in Tapestry at some point.

For example, a lot of the logic for how engine services work will be reformulated into a chain of filters. This will allow code to be shared, but also allow you to plug into the pipeline to add your own logic to existing services.

Servlets and ServletFilters are another pretty good example; in that case, FilterChain would be the service interface, and ServletFilter would be the filter interface.

Unknown said...

public void MyFilterImpl implements MyFilter
{
public void myMethod(int arg1, String arg2, MyService service)
{
// Do something before ...

service.myMethod(arg1, arg2);

// Do something after ...
}
}

It's a lot like interceptors; the filter can do work before and after the service (which is really the next filter in the chain, in disguise) is invoked. It can also pass different or modified parameters in, and modify the result. Bargain basement AOP.