ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Adam Murdoch" <adammurdoch...@yahoo.com>
Subject RE: IntrospectionHelper request
Date Wed, 09 Jan 2002 11:58:14 GMT


> From: Jose Alberto Fernandez [mailto:j_a_fernandez@yahoo.com]
> 
> From: "Peter Donald" <peter@apache.org>
> 
> > On Tue, 8 Jan 2002 09:20, Jose Alberto Fernandez wrote:
> > 
> > > This is fine. The only thing I would ask (and notice I have 
> not looked at
> > > your code) is whether you plan to use the same mechanism for
> > > TaskContainers. There is certaintly no reason whatsoever to have two
> > > different mechanisms for what it is exactly the same issue.
> > 
> > Theres no distinction between TaskContainers and other sorts of 
> containers. 
> > However I don't think that a similar mechanism will be used. In 
> the case of 
> > TaskContainers et all you are not dealing directly with tasks but with 
> > TaskModels - so in this case it would be much more beneficial 
> to have the 
> > "container" passed the TaskModel and it can interpret it as 
> appropriate. 
> > 
> > The mechanism described above is more orientated to adding a concrete 
> > implementation of a abstract type rather than adding and possibly 
> > implementing tasks according to specified rules and behaviour.
> > 
> > If you can think of a mechanism that would be easy to support 
> both use cases 
> > easily then I am all ears.
> > 
> 
> Once again you speek in riddles :-)
> Maybe I have to spend time on your code, or maybe you could give 
> us an intro
> to the design of myrdom(sp?), you have generated way too much code for me
> to have the time to digest it.
> 
> In any case, when I write a Task that can have elements that are 
> tasks (TaskContainer)
> or one that can have mappers, there should be no difference from 
> the task writer point of
> view on how to write its set/add methods. Of course it may be 
> that for a TaskContainer
> the argument is not ot type Task, but some other thing like a 
> TaskModel that you mention
> and I am not sure I understand. But the point is that whtever it 
> is, it should be based
> on the same discovery mechanism that you describe for the other <roles>:
> "based on the "add" method signature, reverse map to a role and 
> then search on the role's
> registry for an entry for the corresponding element".
> 

At risk of muddying the waters further, I'm going to try to answer this from what I know of
myrmidon (but I may have it wrong).

This is a long-winded look at how task configuration works in myrmidon, and how it deals with
the nested task problem.  And some sugestions for making it a little simpler.

In myrmidon, tasks are instantiated and configured just prior to the task being executed.
 The parameters of the task are instantiated and configured as part of this.  This happens
recursively, so that the parameters of the parameters are configured, and so on.

Configuration is done via introspection, but its a bit simpler than Ant 1's scheme:

For each attribute X, the object's setX() method is called.  The attribute value is converted
from a string to the appropriate type (converters can be dynamically added).

For each nested element X:
  - the nested object is created using the parent object's createX() method (if present),
or the no-args constructor of the datatype.
  - the nested object is configured recursively.
  - the nested object is added to the parent using the parent object's addX() method.

Text content is set by calling the object's addContent() method.

Now, this places a couple of conditions on every object, from the task recursively down through
its children:

1. The object has to be configurable via the introspection scheme.  Now, the configurer is
far from complete - there's things like type substitution and reference resolution to go in.
 But it clearly isn't going to handle every possibility.


2. It has to be a valid time to configure the object, when the outermost task is configured.

A task can only be configured immediately before it is executed.  This means that nested tasks,
as a general rule, cannot be configured at the same time that the outermost task is configured.
 Instead, the nested task's configuration has to be deferred until it is just about to execute.

For example, <sequential> breaks this rule, because only the first nested task can be
configured when the <sequential> task is configured.


Before looking at how myrmidon lets a task or data type get around these restrictions, it
might be worth taking a step back and considering whether it should.

Take the first condition.  I haven't seen a convincing use case that the introspection scheme
doesn't handle, once you add in type substitution.  Maybe it would be good to stick with this
rule for Ant 2.

Ok, the second condition.  We definitely want to have nested tasks.  So we need to come up
with:

- A way for the container to deliver to the task, a placeholder for each nested task.  The
placeholder could be, for example, the nested task's configuration (this is, more or less,
the TaskModel that Peter is speaking of), the task object  itself (unconfigured of course),
or some opaque handle.

- A way for the task to hand the placeholder back to the container, and say "configure and
execute this please".


Here's how myrmidon solves the problem:

Firstly, the task has to implement Configurable:

public interface Configurable
{
    void configure(Configuration configuration) throws ConfigurationException;
}

When the container goes to configure a task, it checks whether the task implements Configurable.
 If so, the container calls configure() and passes the task its entire configuration.  It
then becomes the task's responsibility to configure itself.  The container does not use the
introspection scheme at all.

So the task configures itself however it sees fit.  There is a helper AbstractContainerTask
that you can extend to do a lot of the work for you.  The task does not configure its nested
tasks, but holds on to the Configuration object for them (the Configuration object is the
placeholder, in this case).

When the task wants to configure and execute a nested task, it asks the container for an Executor
(not strictly true, but it'll do for argument's sake).  Executor looks like this:

public interface Executor
{
    void execute( Configuration task, ExecutionFrame frame ) throws TaskException;
}

The task calls execute() and passes it the nested task's configuration.  The ExecutionFrame
lets the task do all kinds of stuff, like controlling the properties available to the nested
task, which logger it uses and so on.  The Executor configures and executes the task, and
returns.


A good solution, certainly very flexible.  A few things I'd like to change:

1. Use reflection to deliver nested task placeholders, something like:

public add(Configuration elem);

or

public addX(Configuration elem);

2. Get rid of Configurable.  Then, all parameters to a task or data type are delivered via
introspection.

3. Maybe change the task placeholders from Configurable to some interface like the following:

public interface Executable
{
    void execute( ExecutionFrame frame ) throws TaskException;
}


Adam


--
To unsubscribe, e-mail:   <mailto:ant-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:ant-dev-help@jakarta.apache.org>


Mime
View raw message