> From: Peter Donald [mailto:[EMAIL PROTECTED]
>
> > Logging would be the main one - say I want to do logging in some deeply
> > nested obect, I need to have something pass me a Logger. Ideally, my
> > object would simply implement LogEnabled, and the container would supply
> > the object with a Logger as part of its "initialisation". The
> alternative
> > would be for the object's parent to supply the logger - this
> would be bad,
> > as it would force the parent to be LogEnabled.
> ...
> > It's possible that a data type might need other components, or the task
> > context, to do its work. Again, having the container supply
> these would be
> > a good thing.
>
> I would like to move away from having sub-elements being
> "intelligent" and
> move towards them just being passive data containers. When you
> need to get
> generate complex information from these items (like processing a
> patternset
> to get a list of files) you pass these passive data items to a
> active service
> (ie FileScanner or something) that would do all the magic for you.
>
I'm not so sure about this one. It's a good pattern, but its necessarily a
good restriction to force on all sub-elements.
We already deal with sub-elements that break this model - for example, anywhere
where a task can be a sub-element (in <project>, <target>, <parallel>,
<try><catch><finally>, whatever). If we can generalise the solution a touch,
then we can deal with arbitrary sub-elements that are "active". It's a trade
off - if we can generalise cleanly, then Ant becomes far more adaptable for it.
If we can't come up with a good generalisation, its not a big deal - right
now, we don't have any active sub-elements that aren't tasks (though, I think
this is more due to Ant 1 limitations, rather than the concept not being
useful). I feel that it is something to at least try.
Here's why I feel splitting passive data container and active evaluator is a
good pattern, but not a good rule:
* It becomes awkward when there is a reasonable degree of polymorphism. In
general, you've either got to have an evaluator which understands every
possible data container (bad, you've lost the main strength of poymorphism), or
a data type implementation has to provide both a container and an evaluator
(bad, extra work for task writers). In many cases, it's simpler just to
provide a data type implementation that encapsulates both config and behaviour.
However, there are other cases where the separation is powerful, where
alternative evaluators can be swapped in, and the data container reused.
* Sometimes the "data type" actually represents a service. For example, a
compiler or a VFS. The data container/evaluator split stops being useful.
* It forces the task writer to do the evaluation - that is, finding an
appropriate evaluator, and doing the evaluation. Ok, not a lot of code in each
instance, but it does add up. Often the task is only interested in the result
of the evaluation, and does not care where the result comes from. In these
cases, having the container supply the result to the task saves work for the
task writer. And gives the container much more flexibility in how to go about
doing the evaluation.
On a completely different topic, I'm wondering whether introspection might be a
better way to deliver services to a task, rather than using Composable and
Contexualizable. By "services" I mean both core runtime services and
task-specific services:
- Loggers.
- Task executor.
- An execution manager (for external commands).
- file system manager (resolving file names, creating file systems, etc).
- Compiler adaptors.
- etc.
It's nicely declarative, and could take advantage of things like the TaskInfo
work, and the configuration stuff above. It would also be an easy win for
allowing user and/or build file control over these things.
It might work something like this: For each service required by a task, a
service instance can be specified in the build file, either inline or by
reference. If an instance is not specified, a default instance is provided by
the container. The container decides on which instance to use:
- The user may provide an instance in their preferences.
- A tasklib may provide a default instance for services it introduces.
- The container may provide default instances for core things like loggers,
etc.
- There may be no default at all.
An example:
public class Javac extends AbstractTask {
public void setCompiler(JavaCompiler compiler) { ... }
}
would allow build files like:
<javac>
<compiler type="jikes" pedantic="true" failOnWarning="true"/>
</javac>
Or, by reference:
<project>
<jikes id="java-compiler" pedantic="true" failOnWarning="true"/>
<target name="compile">
<javac compiler-ref="java-compiler"/>
</target>
<target name="another-compile">
<javac compiler-ref="java-compiler"/>
</target>
</project>
Or, say, the following in my ~/.antrc:
<antrc>
<jikes pedantic="false" emacsOutput="true"/>
</antrc>
Adam
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>