cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Reinhard Poetz <reinh...@apache.org>
Subject Re: Schema of block.xml
Date Tue, 29 Mar 2005 06:14:25 GMT
Daniel Fagerstrom wrote:
> Reinhard Poetz wrote:
> 
>> Daniel Fagerstrom wrote:
>>
>>>
>>> Looks good. What I don't agree with is the single inheritance 
>>> (extends and implements). I think that a typical use case will be 
>>> that you build your webapp as a block. Then it will be quite natural 
>>> to put it together by extending a number of blocks that take care of 
>>> things like skining, user handling, content management etc. You mount 
>>> the URI spaces for these blocks relative to your own block and 
>>> overrides (polymorphically) components and sitemap resources in the 
>>> extended blocks with behaviour in your own.
>>>
>>> We need IMO think a little bit more about what extends and implements 
>>> means in the context of blocks. 
>>
>>
>> yes indeed
>>
>> For example what if block A extends
>>
>>> block B, will the concrete B block be determined first at deploy 
>>> time? IMO it makes sense and creates more flexiblity than the 
>>> extension mechanism in Java or C++. But it is quite different so we 
>>> should try to understand the consequences.
>>
>>
>> ok, let's discuss this based on an example :-)
>>
>> Block A requires block B to work correctly:
>>
>> <block id="http://cocoon.apache.org/blocks/A/1.0.0">
>>  <requirements>
>>   <requires block="http://cocoon.apache.org/interface/pdf/1.0" name="B"/>
>>  </requirements>
>> </block>
>>
>> IIUC this requirement doesn't say explicitly that it requires a 
>> specific block but a block that implements the interface 
>> http://cocoon.apache.org/blocks/pdf/1.0.
>>
>> Let's assume that block B provides PDF-making functionality:
>>
>> <block id="htp://cocoon.apache.org/blocks/B/1.0.0">
>>  <implements interface="http://cocoon.apache.org/interface/pdf/1.0"/>
>> </block>
>>
>> I think Daniel is right here that there is no reason that we need 
>> single-interface implementation. So let's change this to
>>
>> <block id="htp://cocoon.apache.org/blocks/B/1.0.0">
>>  <implements>
>>    <interface id="http://cocoon.apache.org/blocks/pdf/1.0"/>
>>  </implements>
>> </block>
>>
>> (Note to myself: I have to enhance the deployment desriptor so that it 
>> provides information which block _implementation_ should be used for a 
>> specific requirement which is "only" an interface.)
>>
>> To make it clearer that a requirement is an interface, we should 
>> change the requirements descriptor a bit:
>>
>> <block id="http://cocoon.apache.org/blocks/A/1.0.0">
>>  <requirements>
>>   <requires
>>     interface="http://cocoon.apache.org/interface/pdf/1.0"
>>     default="http://cocoon.apache.org/blocks/pdf/1.0.x"/>
>>     name="B"
>>    />
>>  </requirements>
>> </block>
>>
>> Note that I added a "default"-attribute to make auto-deployment possible.
>>
>> I changed the cob schema according to the current state of discussion.
> 
> 
> Sounds reasonable this far. To illustrate for the rest of the readers 
> why this is not FS, consider a Wiki block that among other blocks 
> extends a "default skin" block, then it makes sense to make it possible 
> at deploy time to change the default skin to a "new improved skin" that 
> implements the same inteface.
> 
>> What we haven't discussed yet is what does an *block interface 
>> definition* look like? Is this an explicit definition that desribes 
>> all contracts or a very loose marking of a block in its block.xml? Ideas?
> 
> 
> IIRC, Stefano's idea about this was that it is a to complicated problem 
> to solve at this stage, and that the interface is just the URI and maybe 
> some documentation.
> 
> We could of course have something like a list of the exported components 
>  together with what interface they implement, the exported URIs together 
> with query parameter requirements and a "scheme" for posted and response 
> data. But such a strongly typed interface would make it rather 
> unatractive to start developing blocks. And we don't have much 
> infrastructure support for schema handling right now.
> 
> For the time being I think we should postpone this problem until we get 
> more experience with block develpment, then we could have some kind of 
> optional interface along with what I described above.

Fine for me

> One of the problems with handling stronger contracts as I see it is that 
> the sitemap is to procedural, you cannot ask the sitemap what URIs, 
> reuest params, input and ouput schemes etc, it supports. We would need 
> something like the site.xml in Forrest to be able to describe an 
> interface and to be able to check if a block really implements an 
> interface.
> 
>>                                - o -
>> More difficult is block extending. We have to consider what extension 
>> means for several aspects:
> 
> 
> First I think that polymorphism should be possible to use for all kinds 
> of sevices, otherwise extension becomes to unflexible to be useful. 

ok

> This 
> is not just symmetry thinking, we are using a simple form of sitemap 
> extesions in the webapps at my company, and polymorphism is a must. 
> Question is if it should be done by default or be an option. I would 
> suggest optional as you probably want to design what should be polymorph 
> and that the overidable services is part of the (non existing) interaface.
> 
>>  - services:
>>    * components
> 
> 
> A component "foo" is taken from the blocks own CM with "self:foo" or 
> just "foo" if we have that behaviour as default. A component will be 
> found polymorphically by "polymorph:foo". In the polymorph case where A 
> extends B and B extends C, if "poymorph:foo" is used in C the block 
> manager will first ask the CM in A for "foo" then the one in B and at 
> last in C.
> 
> For the case where A extends B,C,D,E, for "self:foo" in A the block 
> manager will first ask the CM in A and if it is not there we must impose 
> a search order on B,C,D and E, e.g. the order in which they are declared 
> in the extensions list in block.xml for A.

I'm not sure if we really need this. Sounds like FS to me but maybe you can give
an example where this would be prefered over delegation (blockA requires blockB,
blockC, blockD and blockE and if you need a component in blockA and you don't 
know whether it is in blockB or blockC you have to call it explicitly by

comp = manager.getComponent("blockB:componentXYZ");
if(comp == null) {
   comp = manager.getComponent("blockC:component:XYZ");
}

This also makes it possible to use different orders and you don't stick to a
single order declared in an extensions list in block.xml.


>>    * sitemaps/pipelines
> 
> 
> This works like the components, but we have "block:self:" and 
> "block:polymorph:" protocols. You can use
> 
> <map:generate src="block:polymorph:/site.xml"/>
> 
> e.g. in your Forrest block to make it possible for blocks that extends 
> on that block to overide the "site.xml". Now the question is where and 
> in what priority order the sitemaps in blocks that you extend should be 
> "mounted". Of course we could do something simple like having an 
> implicit pass through mount at the same URI space as the extended block 
> in the end of its own URIs. But I don't think that is flexible enough, I 
> would propose that one instead explicitely mounts the blocks that one 
> extends with "map:mount/@block" in the sitemap. 

good idea

> Then one can decide to 
> mount the blocks in a "sub directory" and to do common error handling 
> after all the block are tried.
> 
>>    * flowscripts
> 
> 
> If we follow the ideas about flowscripts as components as I proposed in 
> the discussion in the beginning of this year, the discussion about 
> components above aplies.
> 
>>  - deployment
>>
>> As I grew up with Java I have only used single-inheritance yet. We had 
>> a long discussion what inheritance means for flowscripts (e.g. we have 
>> to support super() calls) at the beginning of this year.
>> I think we need the same discussion for components and pipelines too. 
>> Stefano already gave an example what inheritance could mean for 
>> piplines (http://wiki.apache.org/cocoon/BlocksIntroduction):
>>
>> ------------------------------------------------------------------------------ 
>>
>> Improvement #3: block inheritance
>>
>> The third step is to allow blocks to extends other blocks.
>>
>> The idea is to be able to wrap a block with another one, creating an 
>> 'overloading' mechanism similar to the one used by OOP inheritance 
>> where methods are 'fall back' to the extended class if the extending 
>> class doesn't implement them.
>>
>> Let us supposed we have the following block (very simple):
>>
>> block "A" implements "skin"
>>
>>          /stylesheets/changes2document.xslt
>>          /stylesheets/faq2document.xslt
>>          /stylesheets/document2html.xslt
>>          /resources/logo.gif
>>
>> and let us suppose that we want to change the look and feel of that 
>> block. The first two stylesheets provide simply a way to adapt from 
>> more specific markup to the Document DTD. So, my block would need to 
>> change only the last two resources 'document2html.xslt' and 'logo.gif'.
>>
>> The best solution is to allow my block to explicitly "extend" that 
>> block and inherits the resources that it doesn't contain.
>>
>> block "B" extends block "A"
>>
>>          /stylesheets/document2html.xslt
>>          /resources/logo.gif
>>
>> but then block B still is considered implementing behavior "skin" 
>> because the rest is inherited.
>>
>> This mainly:
>>
>>     * reduces block development and maintanance costs because changes 
>> and bugfixes are directly inherited by all the extending blocks, thus 
>> allowing better SoC between the two groups mainaining the different 
>> blocks
>>
>>     * easy customization: blocks can be adapted for personal specific 
>> needs simply with a wrapper around and without the need to repackaging.
> 
> 
> Ok, allready discussed that above. I think we need to be more explicit 
> about what behaviour we want. If we just write:
> 
> <map:transform src="stylesheets/document2html.xslt"/>
> 
> that means normally the same as:
> 
> <map:transform src="context:/stylesheets/document2html.xslt"/>
> 
> and I don't think it is a good idea for isolation between block to be 
> able to overide what is in the current context, only things that are 
> exposed through the sitemap should IMO be overidable:
> 
> <map:transform src="block:polymorph:/stylesheets/document2html.xslt"/>.

I'm not sure if Stefano and you mean the same here. IIUC in Stefano's example
you have e.g. blockA and blockB. blockA implements the behaviour skin and
extends blockB:


<block id="http://cocoon.apache.org/blocks/A/1.0.0">
  <extends>
   <block id="http://cocoon.apache.org/interface/B/1.0.0"/>
  </extends>
  <implements>
    <interface id="http://cocoon.apache.org/interface/skin/1.0"/>
  </implements>
</block>

A sitemap snippet of blockA:

<map:match pattern="stylesheets/document2html.xslt">
   <map:read src="{0}"/>
</map:match>

A sitemap snippet of blockB:

<map:match pattern="stylesheets/changes2.html.xslt">
   <map:read src="{0}"/>
</map:match>
<map:match pattern="stylesheets/document2html.xslt">
   <map:read src="{0}"/>
</map:match>


Another block, e.g. blockC now uses blockA for its styling:

<block id="http://cocoon.apache.org/blocks/C/1.0.0">
  <requirements>
   <requires block="http://cocoon.apache.org/interface/skin/1.0"
    default="http://cocoon.apache.org/blocks/A/1.0.0"
    name="skin"/>
  </requirements>
</block>

Here a sitemap snippet of blockC:

<map:match pattern="myPipeline1">
   <map:generate src="bla"/>
   <map:transform src="blocks:skin:/stylesheets/document2html.xslt"/>
   <map:serialize/>
</map:match>
<map:match pattern="myPipeline2">
   <map:generate src="bla"/>
   <map:transform src="blocks:skin:/stylesheets/changes2html.xslt"/>
   <map:serialize/>
</map:match>

myPipeline1 gets the stylesheet from blockA and myPipeline2 from blockB (no
stylesheet available in blockA --> fallback to super block blockB)

Does this make sense for you? How does block:polymorph apply in this usecase?


>> ---------------------------------------------------------------------------------

>>
>>
>> I'm not sure about multiple-block inheritance. For me it's some kind 
>> of an anti-pattern but maybe I'm too Java-minded.
> 
> 
> Multiple inheritance is not an antipattern in itself. The idea that it 
> should be is IMO mainly marketing BS from Sun to make the stupid idea of 
> single implementation inheritance seem like a feature.
> 
> But there are a number of antipattern that was popular in OO maybe 10-20 
> years ago that was based on multiple inheritance. But that is a 
> different thing. People tended to model the world with classes instead 
> of interfaces, and used deep an multiple inheritance that got a lot of 
> garbage from all the to full featured base classes. Also as the 
> extension relation in Java (and many other OO languages) is much 
> "harder" than using through interface, extension was considered less 
> flexible.
> 
> But as discussed above we don't need extension to be as hardcoded as in 
> OO languages. And also implementation inheritance is a rather natural 
> and efficient way to put things together.


I think that modelling applications using interfaces and implementing them using
delegation is better than extension ... The question for blocks now is what
multi-block inheritance really buys us. As I said above I think we should go
through all 3 block services (pipelines, components, flows) and describe
scenarios and compare pros and cons of multi-block inheritance compared to 
single-block inheritance.

-- 
Reinhard Pötz           Independent Consultant, Trainer & (IT)-Coach
{Software Engineering, Open Source, Web Applications, Apache Cocoon}

                                        web(log): http://www.poetz.cc
--------------------------------------------------------------------



Mime
View raw message