ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Costin Manolache <cmanola...@yahoo.com>
Subject RE: Roles (was: antlib)
Date Mon, 28 Apr 2003 15:03:45 GMT
Jose Alberto Fernandez wrote:

>> You'll have a task TaskA, with a method "addRoleB".
>> And in XML:
>> 
>>   <taskA ...>
>>      <implementationB1 >
>>   </taskA>
>> 
>> TaskA doesn't know anything about the implementation - it
>> will only use an
>> interface ( or base class ) "RoleB" as parameter.
>>  
>> 
>> I assume you will need some way to associate the tag
>> <implementationB1> with
>> a particular class - and this can be very well be done currently with
>> <taskdef> or <typedef> ( or with the new "role" declaration
>> if you want ).
> 
> The declaration of the class for <implementationB1> as a member of the
> role is what you do equivalent to <taskdef> or <typedef>.

Why is this required ? 

<typedef> should be enough for most common cases - the "role" declaration is
already included in the .class ( i.e. interfaces implemented by the class )


>> In any case, all you really need is the tag name and the
>> class name - the
>> roles will be available as interfaces or superclasses.
>> Nothing special for
>> this association.
>> 
> 
> No. If you do it this way it would mean that you cannot expose diferent
> names for the same class depending on the role. It will also mean that

That's not true. You can declare a class with as many names you want. You
can have multiple typedef and taskdef, each with a different name, but same 
class.


> thing that just happen to implement a role (just because of some
> inheritance) will be made available even though it makes little sense
> doing so. Ex. <if>.
> 
> <if> is a task. It makes little sense to allow it as a condition,
>  but because it is implemented by subclassing ConditionBase
> which would implement the role for conditions, it would mean you will
> declare it in the condition role. (This seems wrong in principle).

Not sure I understand the <if> example. My feeling is that <if> should
be useable in a condition role. 

In general - if you extend a class or implement an interface you assume 
a specific contract. Implementing an interface ( directly or by inheritance)
and not supporting that particular role is wrong from an OO programming
point of view.



> Secondly, since classes do not need to implement the interface but
> may be adapted, you cannot assume much there either.

I think I mentioned - this is a special case and it clearly requires a 
special declaration ( which associates the adapter with the class ).

Something like:
  <taskdef name="tomcatEngine"
           class="org.apache.catalina.core.StandardEngine"
           adapter="org.apache.commons.modeler.ant.JMXTaskAdapter" />



>> The second part is making IntrospectionHelper recognize  a child that
>> doesn't fit the current patterns ( i.e. no addImplementationB1 ), and
>> instead of reporting an error look up the child by the name
>> and create the
>> type, then check the interfaces implemented by the class and
>> see if any
>> method in the parent matches.
>> 
>> Is there anything else ?
>> 
> 
> You can only check for those interfaces where the class was registered
> to use that element name (you cannot blindly look at all possible
> interfaces).

Why do you think it is "blindly" ? You have a parent that has a:
   setFoo( Interface1 )
and a child that implements Interface1. 


We could have both the redundant <role> and the interface - if you want
explicit control ( for example prevent a class for working in a role,
even if it implements that interface ). I don't see the use case, but
if you really need it. 

My argument is more for the "common" or "simple" case - which I want 
to be as simple as possible.  For your particular use case - using the
interfaces would require the least ammount of work and seems very 
natural. You declare that a task implements an interface - that means
( usually ) it can be used in that role, just like any other class
 that implements that interface.

If you implement Runnable - it is supposed that you can be used as 
a parameter to Thread. I feel it's a programming error to implement an
interface but not support its contract, and it's wrong to require 
an additional declaration ("I really implement the interface"). 



>> > 2) What do they do that is no possible in ANT:
>> > 
>> > They allow IntrospectionHelper to connect an XML subelement
>> eventhough
>> > introspection cannot find a create or add/Configured method for it.
>> > It is a well typed methanism, the parent object will only be passed
>> > objects that it knows how to deal with. And the parent
>> object does not
>> > need to have any knowledge of what currently available
>> members are on the
>> > role.
>> 
>> Yes, parent needs one addXXX method with a typed parameter,
>> and the child
>> needs to be matched against it.
>> 
>> 
>> What I don't understand is why should we just use the
>> existing typedef and
>> just add this new pattern to IntrospectionHelper.
>> 
> 
> Because the proposal declares on a Role by role basis. There is
> no assumption the same name will be used for all roles,
> nor that you want to always register all possible roles.

??? 

My point is that "implements" can be used to solve the proble you
describe. You can also solve it as in the proposal ( by using the redundant 
declaration ). In both cases you can use as many names as you want.


>> > a) A role is defined by an interface. This interface is the
>> parameter
>> > for a new special family of addConfigured(<interface>) methods.
>> 
>> +1 on role defined by an interface ( or base class - I don't
>> see any reason
>> to restrict it to only interfaces ).
>> 
> 
> Looks neater :-). But I have no big hangups as long as we can clearly
> distinguish between named element add/create methods and the ones for
> roles.

If you have an addFoo() and a <foo> child - I assume the old pattern 
will be used, not the roles ( backward compat, etc ). 

The roles would apply if you have a <foo> child and no addFoo() - 
you first create <foo> using the typedef/taskdef/componentDef, then
look at the interfaces and find the addInterface() method.



>> I'm not sure a special name is actually required (
>> addConfigured) - just an
>> add or set method with the interface/class as parameter, and
>> then match
>> them on type.
>> 
> 
> the idea of using "addConfigured" as the name in because I think we
> need to be clear on the parsing rules for this objects. Allowing
> passing not configured objects seem to make little sense since
> the implementation is completely blackboxed to the parent node.

What about add[INTERFACE_NAME]( INTERFACE_NAME ) ?


> Also, "set" methods are for attributes only (has that changed?)
> so they do not apply here.

We are changing something by adding the roles, aren't we ? 

set methods work with simple types only - and IMO if we add roles,
setROLE( ROLE ) should be used for roles where a single child is 
allowed, while addROLE( ROLE ) for multi-childs.



>> > b) When IntrospectionHelper fails to find a create/add
>> method for the
>> > element, it will look at all the roles used in the addConfigured
>> > methods and on each of those roles will try to find an
>> object declared
>> > with that element-tag name. If one and only one match is found then
>> > the instantiation is successful and the new object will be
>> configured;
>> > otherwise it is an error and parsing stops.
>> 
>> I disagree here. If the role is associated with an interface,
>> then the
>> "declaration" is part of each task/type class. If you
>> implement/extend that
>> interface/class - then you have this role. Any extra declaration is
>> redundant - unless you want to declare a wrapper ( see below ).
>> 
> 
> You do not declare a class, you declare what does the element-tag
> means. I.e., "in the context of Interface X the name 'implementationB1'
> means to use class Y with adaptor Z if necessary".
> But "in the context of Interface P the name 'implementationB1' means to
> use class Q with adaptor R if necessary".

Too complex.

Let's not mix the adaptor.

BTW - you seem to assume that adaptor is specific to a role, when in fact 
it is specific to a class. You can have many adapters for the Task role.



>> > c) The configured object may or may not implement the Role
>> interface,
>> > if it does not, an Adaptor object may be instantiated as a proxy
>> > for the object. Which adaptor is used depends on how the
>> implementation
>> > was declared.
>> 
>> +1 here - I think the "taskdef/typedef" declarations ( which
>> I would like
>> merged in a single "component" declaration ) should take an
>> extra "adapter="
>> attribute, to allow this wrapping.
>> 
> 
> The problem of specifying an adaptor on a declaration by declaration basis
> is that the whole bussines becomes very tedious. And you have to know
> for each class whether the adaptor is needed or not. In particular for
> datatypes that many of then need to function as tasks also but implement
> nothing they all need to define the same adaptor. Instead the adaptor
> association is done when defining the role name. (This, by the way, is
> what in escence the current <typedef> does).

That's very, very wrong. 

The adaptor/wrapper is specific to the implementation class. I use one
adapter to make a JMX MBean useable in a Task role, another adapter
for a class with execute() method, etc. 

Having one adapter per role is absurd. 



>> > Declaration of roles:
>> > ====================
>> > 
>> > A role definition associates a name with an
>> (Interface,Adaptor) pair.
>> > The only reason for associating a name with the role is to
>> ease notation
>> > when declaring members of a role.
>> 
>> Why ?
>> 
> 
> For the syntax sugar reasons I explained above. In reality it is more
> convinient.

I strongly disagree with the Interface <-> Role relation ( or with 
the "convenient" attribute ).

Convenient is to use the information already available.



>> All the user needs to do is associate a name with an
>> implementation class.
>> You need the impl. class anyway - and so the name, but the roles will
>> be available as soon as you have the impl. class. You only need an
>> additional attribute if you want to wrap.
>> 
> 
> As explined above this assumes the same name used everywhere, which it is
> not the case.

In general - context-sensitive languages are trickier, but you can even
today use different names for the same class. 

You can also have the same name associated with multiple classes in
different contexts- by the addFoo() pattern - but I wouldn't want to 
go in this direction.



>> I think we should either use <taskdef>/<typedef> - with the additional
>> attribute for the adapter - or use a new element <component>
>> ( or even
>> <role> - but I don't find this very intuitive ) that will replace both
>> <taskdef> and <typdef>.
>> 
>> For example:
>>  <component name="elementName" className="...." [adapter="...."] />
>> 
> 
> I do not mind having a <component> but with the syntax:
> 
> <role name="roleName" className="...." [adapter="...."] />
> 
> <component name="elementName" role="roleName" classNane="....." />
> 
> <role> could be just <component role="role" .../> but I think is less
> clear.

Ok, that's something we stand on totally oposed positions.

It just doesn't make sense to me having the adapter associated with the 
role.

 <component name="elementName" className="...." [ adapter="...." ] />
 
BTW, my prefference would be to actually just use <typedef> as the
definer ( with the additional optional adapter attribute ). But I'm
fine with a new element name.


Costin


Mime
View raw message