You are viewing a plain text version of this content. The canonical link for it is here.
Posted to docs@cocoon.apache.org by Apache Wiki <wi...@apache.org> on 2005/09/14 15:34:47 UTC

[Cocoon Wiki] Update of "ServiceAccessPattern" by JCKermagoret

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Cocoon Wiki" for change notification.

The following page has been changed by JCKermagoret:
http://wiki.apache.org/cocoon/ServiceAccessPattern

New page:
= Intent =
This pattern's goal is to have a very loose coupling between cocoon services and the data provided by the generator.

= Motivation =
When developing web applications, we usually quickly prototype a solution for one context. Often, requirements evolve and multi context support is needed.

This pattern provides a systematic approach to make services multi context aware, with an elaborated fallback mechanism to manage specific, then common, then default (for example) configuration.

Moreover, all the information where mapping from name services to services' paths are centralized in one location only. Things are easy to update and maintain.

= Architecture =
attachment:loose-coupling-dataaccess-dp.gif
= Implementation =

== Tight coupling ==
It is a very simple pipeline.

attachment:tight-coupling-dataaccess-dp.gif

{{{
    <map:match pattern="hello.html">
    	<map:generate src="docs/hello.xml"/>
        <map:transform src="stylesheets/convert2html.xsl"/>
        <map:serialize type="xml"/>
    </map:match>
}}}

This web app works well, but if your boss suddenly tells you : "Well, things are changing quite a bit. The customer wants one web app to serve multiple hosts. And the 'hello' may change according the user's role."

Of course, there are a lot of services beside the hello one with the same kind of requirement. You have to update every service. So, after a quick brainstorming, you suggest to create a context directory under docs one. And you immediately go back to work and do it :

{{{
    <map:match pattern="hello.html">
    	<map:generate src="docs/{request:serverName}/hello-{request-param:role}.xml"/>
        <map:transform src="stylesheets/convert2html.xsl"/>
        <map:serialize type="xml"/>
    </map:match>
}}}

At this point, you think it may be a good idea to centralize data for each context in one location only. You immediately go back to work and create the following :

{{{
    <map:match pattern="hello.html">
	<map:generate src="context://customer/{request:serverName}/hello-{request-param:role}.xml"/>
        <map:transform src="stylesheets/convert2html.xsl"/>
        <map:serialize type="xml"/>
    </map:match>
}}}

But you have to update every service again.

After a few cycles like this one, you're a little fed up with all this mess, and the always evolving requirements. So, you decide to be agile and build a medium coupling solution :

== Medium coupling solution ==

attachment:medium-coupling-dataaccess-dp.gif

{{{
    <map:match pattern="getData-*">
	<map:generate src="context://customer/{request:serverName}/hello-{request-param:role}.xml"/>
	<map:serialize type="xml"/>
    </map:match>

    <map:match pattern="hello.html">
    	<map:generate src="cocoon:/getData-hello"/>
        <map:transform src="stylesheets/convert2html.xsl"/>
        <map:serialize type="xml"/>
    </map:match>
}}}

In this solution, you only to update the getData-* pipeline to reflect new requirements. But you have to do that for all your applications (all your sitemaps).

== Loose coupling solution ==

attachment:loose-coupling-dataaccess-dp.gif

{{{
    <map:match pattern="getData-*">
	<map:generate src="cocoon://locator/getData-({request:serverName})-{1}"/>
	<map:serialize type="xml"/>
    </map:match>

    <map:match pattern="hello.html">
    	<map:generate src="cocoon:/getData-hello"/>
        <map:transform src="stylesheets/convert2html.xsl"/>
    	<map:serialize type="xml"/>
    </map:match>
}}}

And you have in the locator directory at the root of your cocoon app the following :

{{{
    <!--|
        | {1} : context (for example serverName, but it could be application name)
	| {2} : criteria
	|-->
    <map:match pattern="getData-(*)-*">
        <map:select type="resource-exists">
              <map:when test="{config:/project-root}/{1}/author/{2}.xml">
                  <map:generate src="cocoon://project/{1}/author/{2}.xml"/>
              </map:when>
              <map:otherwise test="{config:/project-root}/{1}/anonymous/{2}.xml">
                  <map:generate src="cocoon://project/{1}/anonymous/{2}.xml"/>
              </map:otherwise>
        </map:select>
        <map:serialize type="xml"/>                    
    </map:match> 

}}}

You can improve the fallback mechanism by adding new situations for anonymous user for example :
{{{
              <map:when test="{config:/project-root}/{1}/reader/{2}.xml">
                  <map:generate src="cocoon://project/{1}/reader/{2}.xml"/>
              </map:when>
}}}

Now, to solve new requirements, you just need to update the locator sitemap.

If your boss tells you the customer wants the information in a database. Don't worry, just update your locator sitemap (but you will need in this case to update the resource-exists action too).


JCKermagoret