ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Jose Alberto Fernandez" <jalbe...@cellectivity.com>
Subject RE: Roles (was: antlib)
Date Mon, 28 Apr 2003 17:40:30 GMT
> From: Costin Manolache [mailto:cmanolache@yahoo.com]
> 
> Jose Alberto Fernandez wrote:
> 
> 
> >> 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.
> 

The point is that with your approach <typedef> will tie the name to that
class on ALL interfaces implemented by the class. On the other hand,
in my approach the mane is tie to the class on a role by role basis.

Assume class C implements role intrefaces P, Q, and R then

	<typedef name="C1" classname="C"/>
	<typedef name="C2" classname="C"/>

will cause two definitions for "P" and "Q" each. There is no way to
assign different names separately. On the other approach:

	<role name="p" classname="P"/>
	<role name="q" classname="Q"/>

	<p name="C1" classname="C"/>
	<q name="C2" classname="C"/>

here you can get different names for different roles if you so wish.

> 
> > 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.
> 

What is the meaning of:

	<condition property="x">
	  <and>
	    <if>
		<istrue value="yes"/>
		<then><echo>yes</echo></then>
		<else><echo>no</echo></else>
	    </if>
	    <istrue value="yes"/>
	  </and>
	</condition>

"x" should be set, but shall "yes" be echoed? Although <if>
implements the contract of condition, the meaning of <if> is to
execute its nested tasks also, but for that <condition> will have to
call the execute() by looking at if as a task and not as a condition.

So the whole construct falls appart.


> 
> 
> > 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" />
> 
> 

So, how you manage the current data-types which can be used as tasks?
Backward compatibility says it needs to be treated internally, but your
notation suggests we would have to declare them passing a TaskAdapter
otherwise we keep the special cases around in core.

With the way the proposal does it, we get the cruft out of core
and the declarations will continue to be as before because the
adaptor association occurs at the role-name level.

> 
> >> 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. 
> 

The case of the <if> for starts. I would like to get an error if I
tried to use an <if> as a condition because if was not designed to
be used that way. And we have to deal with the code we already have.

> 
> 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. 
> 

I think we need it. But I wouldn't mind to provide syntactic sugar to allow
saying add this class to every possible role it implements (as far as the system knows).

> 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"). 
> 
> 

Remember part of the problem is that we need to deal with the code that is already out there

which is not neat at all on these respects.

> 
> >> > 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.
> 
> 

You cannot do it this way. Because <foo> may mean different classes
depending on the roles of interest. Think of the <weblogic> element
of <ejbjar> and <jspc> they are different APIs but the element is named
the same. So you first need to collect what possible roles are posible
at this point (by looking to all the "addInterface" as you call it).
Then we can go and look for <foo> on those particular interfaces.
If at the end you find one and only one unambiguosly then you succeeded.

BTW, the reason I would prefer to use "addConfigured" is because we have already
taken over that method name as one of our patterns, and hence is safer for
BC. Using "addInterface" or "addRole" my clash with somebodies task
that uses those names legitimately today.

> 
> >> 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 ) ?
> 
So you mean something like:
	addorg.apache.tools.ant.role.Condition(org.apache.tools.ant.role.Condition)? :-(

when I said the role is the interface I meant it. I see no reason why
the name of the interface has to be part of the method 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.
> 

I just do not want to create more confusion on users. The rules today
are quite simple and easy to follow. set for attributes, add/create for 
subelements. It is a very simple rule. I do not want some new special case
that only applies roles. If or when set methods are allowed for
single elements or attributes, then we can generalize it to roles also.

> 
> 
> >> > 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.
> 
> 

The interface for Tasks can have as many adaptors as you want
using diferent names to refer to them. Think of role names really
as role aliases. Aliases for the same interface, each providing a
different adaptor, if needed.

> 
> >> > 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. 
> 

See the notes above. It is not per role, itis per role-alias
it is just a way to give some order to the madness we currently have.
Think of all the data-types out there that do not implement any
interface whatsoever, just have an execute method. They all adapt the same way
and you just want a default adaptor that gets applied without you needing to say it
over and over.

> 
> >> 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.
> 

Yes we disagree, what you present will not solve the case of vendor specific
elements like <weblogic> or <jboss> whose meaning depends on the context.

Would you allowed something like:

	<component name="jboss" classname="jboss.ebjjarAPI"/>
	<component name="jboss" classname="jboss.jspcAPI"/>
	<component name="jboss" classname="jboss.serverdeployAPI"/>

Now are you changing the definition of "jboss" or are you adding
distinct definitions for distinct roles. How would anone understand
what this means? 

Still we need this if we want to be able to send the responsability for 
maintaining vendor specific code back to the vendors.

Roles came up from the need to achieve exactly that.

Adaptor came from looking at the actual mess we have in our type system
and try to put some standarized mechanism that can dealt wih it.

Jose Alberto

Mime
View raw message