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

Reply via email to