You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@avalon.apache.org by Leo Simons <le...@apache.org> on 2003/06/11 11:52:43 UTC
merlin's meta tags and a bigger LCD (was: Re: why so many doclet
tags?)
Stephen McConnell wrote:
> Getting in place a full, complete and consitent tag set isn't such a big
> deal - the problem is that there is strong restance to the establishment
> of a standard tag that is not backed by a supporting implementation.
Where? Who? The only alternative I can think of is to build an
implementation then declare that the standard.
> Only works if we cater for all of the requirements.
> I.e. the LCD approach fails the usability test.
it fails some tests, not all. A LCD is always less usable, but the point
is that it is still usable. For example, in merlin, versions might
default to 1.0.0 if left unspecified, names might default to lowercased
classnames, @avalon.dependency mapped into @avalon.meta.dependency and
@avalon.service mapped into @avalon.meta.service, resulting in a fully
functional LCD.
> assertion: we (I) do have sufficient use case knowledge
> Its not about the application scenarios - its about declaring the
> component meta info.
Let's see what you got....the below essay also up on the wiki @
http://nagoya.apache.org/wiki/apachewiki.cgi?AvalonMerlinMetaTags for
(perhaps) more comfortable reading.
cheers!
- Leo
comments on....
= http://avalon.apache.org/sandbox/merlin/tools/tags/index.html =
First of, it's not a good idea to use @avalon when it's only supported
by merlin: this is namespace pollution. But we've been over all that before.
== @avalon.meta.namespace ==
class Enables client modification of the tag namespace.
"Javadoc tags may not include line breaks. As such, it is convinient for
the client to declare an alternatice namespace to the default
avalon.meta." ^^^^^ spelling ^^^^^
=== comments ===
eh...
* @param x the x-coordinate of the northwest corner of the
* destination rectangle in pixels
(from the javadoc tool description) contains a newline, followed by some
spaces. Javadoc tags _may_ include linebreaks, in a fashion similar to
MIME or HTTP headers. So that removes most of the need. Further, I think
that, if you want to replace the namespace with something shorter (which
is just a bad idea because it doesn't read nicely), you would want to
specify that once for a whole set of sourcefiles, rather than inside
each sourcefile.
== @avalon.meta.version ==
class Identifies a class or interface are a Type or Service.
<type><info><version>1.3.0<version></info></type>
=== comments ===
- why is there a <version/> for classes?
- can you depend on a version of a class? Why?
- why is the standard '@version' tag not usable for this purpose?
== @avalon.meta.attribute ==
class A attribute associated with a containing type or service.
<service>
<attributes>
<attribute name="description" value="an example"/>
<attribute name="color" value="red"/>
<attribute name="priority" value="normal"/>
</attributes>
</service>
=== comments ===
Is it not better to simply make all unknown javadoc tags into an
"attribute"? IE:
/**
* @my.tag jo!
* @my.second.tag blah blah blah
*/
resuls in
<service>
<attributes>
<attribute name="my.tag" value="jo!"/>
<attribute name="my.second.tag" value="blah blah blah"/>
</attributes>
</service>
In particular, this means the whole set of tags available through
XDoclet might be provided like this:
/**
* @ejb.bean
* name="bank/Account"
* type="CMP"
* jndi-name="ejb/bank/Account"
* local-jndi-name="ejb/bank/LocalAccount"
* primkey-field="id"
*
*/
and the merlin tool might turn that into
<service>
<attributes>
<!-- clash between "name" here.... -->
<attribute
tagname="ejb.bean"
name="bank/Accound"
type="CMP"
jndi-name="ejb/bank/Account"
local-jndi-name="ejb/bank/LocalAccount"
primkey-field="id">
name="bank/Account"
type="CMP"
jndi-name="ejb/bank/Account"
local-jndi-name="ejb/bank/LocalAccount"
primkey-field="id"
</attribute>
</attributes>
</service>
More in general, javadoc tags are attributes already, so why specify a
special kind of "attribute" to mark an attribute as an attribute?
Similarly, <attribute/> feels icky, since an xml attribute is defined as
<element attributename="attributevalue"/>
== @avalon.meta.name ==
class Declaration of a component type name.
The name tag associates a name to a component type. The name tag is a
required when generating a type descriptor.
=== Comments ===
That's a bit vague; the type DTD got me
<!--
The component element describes the component, it defines:
name the human readable name of component type. Must be a string
containing alphanumeric characters, '.', '_' and starting
with a letter.
... -->
I think "human-readable" implies a free form string. IIUC, name is used
to hold a reference to a type, component, service, in map structures (ie
HashMaps, service managers, etc), but that only requires uniqueness, not
anything else.
If it is _not_ used for this, then what is it used for at all?
== @avalon.meta.lifestyle ==
class Declaration of the lifestyle policy.
The optional lifestyle tag associates a lifestyle policy with a
component type. Recognized lifestyle policies include 'singleton',
'thread', 'pooled', and 'transient'.
/**
* Example of the declaration of a lifestyle policy within a component.
*
* @avalon.meta.version 1.0
* @avalon.meta.name sample
* @avalon.meta.lifestyle transient
*/
results in:
<type>
<info>
<version>1.0.0</version>
<name>sample</name>
<attributes>
<attribute name="urn:avalon:lifestyle" value="transient"/>
</attributes>
</info>
</type>
=== comments ===
the above sample provides ambiguity (example:
/**
* @avalon.meta.attribute name="urn:avalon:lifestyle" value="transient"
*/
). I guess the implication is that "everything inside '@avalon.meta' is
an attribute in the 'urn:avalon' namespace, with any '.' replaced by
':'; we just use some shorthands when parsing into xml". That means the
above two examples should be identical. Are they? Are they both valid?
This smells like it could be confusing.
== @avalon.meta.service ==
class Service export declaration from a type.
This maps to '@avalon.service' from AMTAGS directly:
* @avalon.meta.service type="net.osm.vault.Vault;
* @avalon.meta.service type="net.osm.vault.KeystoreHandler:2.1.1;
note the typo, this should be:
* @avalon.meta.service type="net.osm.vault.Vault"
* @avalon.meta.service type="net.osm.vault.KeystoreHandler:2.1.1"
=== comments ===
Of course, the ":2.1.1" is there for a reason. The example implies this
is not a freeform string, but rather a specific version of a service may
be exported by providing ":2.1.1". Especially since no prefix results in
auto-addition of ":1.0.0". This seems like a bad idea. These things are
seperate and introducing another kind of construct (the ':' within a
value to seperate values) seems much worse than
* @avalon.meta.service type="net.osm.vault.Vault" version="1.1.1"
<type>
<info>
<version>5.1.0</version>
<name>vault</name>
</info>
<services>
<service type="net.osm.vault.Vault"
version="1.1.1"/>
</services>
</type>
Better stil, since the code will do
class MyVault implements net.osm.vault.Vault
it is probably a better idea to have the tool determine what version of
Vault is exported by taking a look at what version of Vault is on the
compile path. That requires some work of course.
== @avalon.meta.stage ==
class Lifecycle stage dependency declaration.
=== comments ===
this is container-specific. Or perhaps excalibur-lifecycle-specific.
== @avalon.meta.extension ==
class Lifecycle stage handling capability declaration.
=== comments ===
this is container-specific. Or perhaps excalibur-lifecycle-specific.
== @avalon.meta.logger ==
enableLogging() Logging channel name declaration.
/**
* Supply of a logging channel to the component.
* @param logger the logging channel
* @avalon.meta.logger name="system"
*/
public void enableLogging( Logger logger )
{
super.enableLogging( logger );
m_system = logger.getChildLogger( "system" );
}
<type>
<info>
<version>2.4.0</version>
<name>component</name>
</info>
<loggers>
<logger name="system"/>
</loggers>
</type>
=== comments ===
by contract, logger.getChildLogger() should work, even without such a
declaration. So why is the declaration there at all? In particular, what
happens if you change to <logger name="blah"/>? Nothing, right?
== @avalon.meta.context ==
contextualize() Declaration of a specialized context class.
/**
* @avalon.meta.context type="net.osm.CustomContext"
*/
public void contextualize( Context context )
throws ContextException
{
CustomContext custom = (CustomContext) context;
...
}
=== commments ===
that should be discouraged! The contextualize() contract is that a
component can expect to receive a Context, nothing /more/, nothing
/less/. I think BlockContext shows how problematic the above is.
== @avalon.meta.entry ==
contextualize() Context entry declaration.
=== comments ===
thought for a bit how this might be combined with
@avalon.meta.dependency. Probably not a good idea.
== @avalon.meta.dependency ==
service() Service type dependency declaration.
=== comments ===
maps to '@avalon.dependency' in AMTAGS.
== More on versioning ==
Lots of unanswered questions.
- do you really need this granular versioning? Isn't versioning per
deployable unit enough?
- when is a service backwards compatible? How do you specify a version
range?
- how are versions actually used in container space? What can you expect
to happen when you declare a version?
- how "optional" can version support be in a container?
== summary ==
use of '@avalon.meta' is a bad idea. s/@avalon.meta/@merlin/ would be nice.
My biggest issue is with versioning. Either go all the way and declare
(for example) that the arguments to some of the tags are urns in the
avalon namespace (and declare the format for urns in that namespace), or
be consistent in application of a pattern.
Also, I'm sceptical as to the need of this granular kind of versioning,
especially with regard to implementations.
=== existing merlin tags ===
@avalon.meta.namespace should just dissapear
@avalon.meta.version should just use '@version', if
needed at all
@avalon.meta.attribute should just dissapear; this stuff
is attributes already. If there's really a need for avalon container
support, just implement a generic algorithm that stores all unknown
attributes (or even all attributes), rather than invent a new
mechanism
@avalon.meta.name needs more specification as to intended
usage, and perhaps should allow free-form strings
@avalon.meta.lifestyle no comment
@avalon.meta.service <-> AMTAGS @avalon.service; concerns
about versioning setup
@avalon.meta.stage tied to excalibur-lifecycle package
@avalon.meta.extension tied to excalibur-lifecycle package
@avalon.meta.logger should just dissapear
@avalon.meta.context has a use case because there exist
components that depend on specific context implementations or
extensions (ie BlockContext), but should be discouraged
@avalon.meta.entry no comment
@avalon.meta.dependency <-> AMTAGS @avalon.service; concerns
about versioning setup
== conclusion ==
The tags currently in use by merlin are not ready for generalization
into a "full suite of tags". I think some things do not make sense, and
in other places contracts are underspecified.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org
Re: merlin's meta tags and a bigger LCD
Posted by Stephen McConnell <mc...@apache.org>.
Hi Leo:
Good to see your hard at work!
Some agreements and some disagreements in-line.
;-)
Leo Simons wrote:
> Stephen McConnell wrote:
>
>> Only works if we cater for all of the requirements.
>> I.e. the LCD approach fails the usability test.
>
>
> it fails some tests, not all. A LCD is always less usable, but the
> point is that it is still usable. For example, in merlin, versions
> might default to 1.0.0 if left unspecified, names might default to
> lowercased classnames, @avalon.dependency mapped into
> @avalon.meta.dependency and @avalon.service mapped into
> @avalon.meta.service, resulting in a fully functional LCD.
LCD fails to deliver the aim of portable declarations.
If a component declares a lifecycle stage then that component should not
be deployable in Phoenix. To ensure this - the Phoenix meta generation
tool MUST recognize a lifecycle stage extension tag. Same story for
lifestyle - it MUST be recognized in order to ensure non-deployment.
LCD simply means potential runtime failure. This makes LCD unusable.
>
> == @avalon.meta.namespace ==
> class Enables client modification of the tag namespace.
<snip>
I aggree that this one can dissapear.
> == @avalon.meta.version ==
> class Identifies a class or interface are a Type or Service.
>
> <type><info><version>1.3.0<version></info></type>
>
> === comments ===
>
> - why is there a <version/> for classes?
It is the declaration of the implememtation version of the class. This
reflects the behavioural contract (as distinct from the interface
contract). A component with a major version incriment refects a
incompatible semantic change which may be independent of the interface
change. For example, semantics may be introduced on the interpritation
of a string argument. The interface reamins the same, but the sematics
of the implementation may have changed in an incompatible manner. This
information can be used by custom selectors and/or management tools.
>
> - can you depend on a version of a class? Why?
An ORB version 1.x is very different to version 2.x. The implementation
version 2.1 a lower level of functionality to 2.4. Yes - you can depend
on this sort of information because it reflects compliance with
implementation semantic constraints.
>
> - why is the standard '@version' tag not usable for this purpose?
The @version tag is typically used to reflect CVS versioning.
Overloading this with formal class implementation versioning would be a
bad thing to do.
>
> == @avalon.meta.attribute ==
> class A attribute associated with a containing type or service.
>
> <service>
> <attributes>
> <attribute name="description" value="an example"/>
> <attribute name="color" value="red"/>
> <attribute name="priority" value="normal"/>
> </attributes>
> </service>
>
> === comments ===
>
> Is it not better to simply make all unknown javadoc tags into an
> "attribute"? IE:
>
> /**
> * @my.tag jo!
> * @my.second.tag blah blah blah
> */
I don't like the idea of bundliung anything that is unknown (I can just
see lots of future problems, not to mention the fact that junk
information will end up on the screens of management tools).
These attributes are used to suppliement a component description with
information that will typically be specific to a container , component
or service. In the general case - attributes should be ignorable - but
there defintition as meta-info attributes should be explicit. This
simply means that if a named value pair is to be associated with a
component type - then there is a standard way to do it.
<snip-examples/>
I like the declaration approach of multi attributes ion a single
declaration. The current approach was largely driven by the assumption
that multiple line usage was not possible (thanks for the correction on
that one).
As far as the type and service instance meta-info declaration is
concerned this could be expressed as:
@avalon.info
somekey="some value"
anotherkey="another value"
>
> More in general, javadoc tags are attributes already, so why specify a
> special kind of "attribute" to mark an attribute as an attribute?
> Similarly, <attribute/> feels icky, since an xml attribute is defined as
>
> <element attributename="attributevalue"/>
Bacause explicit attribute declarations ensure the XML based meta-info
can be validated against a DTD. This means that we have to deal with
known attribute names and elements. The sort of attribute keys and
values we are dealing with here are unknown.
> == @avalon.meta.name ==
> class Declaration of a component type name.
>
> The name tag associates a name to a component type. The name tag is a
> required when generating a type descriptor.
>
> === Comments ===
>
> That's a bit vague; the type DTD got me
>
> <!--
> The component element describes the component, it defines:
>
> name the human readable name of component type. Must be a string
> containing alphanumeric characters, '.', '_' and starting
> with a letter.
> ... -->
>
> I think "human-readable" implies a free form string. IIUC, name is
> used to hold a reference to a type, component, service, in map
> structures (ie HashMaps, service managers, etc), but that only
> requires uniqueness, not anything else.
>
> If it is _not_ used for this, then what is it used for at all?
Within Merlin it provides two functions
(a) the association of a name to the type (e.g. "keystore") that
can be viewed by a management tool
(b) it establishes the default name for an implict deployment
instance
Both name and version tags could be combined under a @avalon.component tag:
@avalon.component name="keystore" version="2.7"
> == @avalon.meta.lifestyle ==
> class Declaration of the lifestyle policy.
> === comments ===
>
> the above sample provides ambiguity (example:
Agreed. This has already come up on the users list. The lifestyle
attribute will be folded into a sub-element of the <info> block in the
near futue.
>
> == @avalon.meta.service ==
> class Service export declaration from a type.
>
> This maps to '@avalon.service' from AMTAGS directly:
Yep.
>
>
> * @avalon.meta.service type="net.osm.vault.Vault;
> * @avalon.meta.service type="net.osm.vault.KeystoreHandler:2.1.1;
>
> note the typo, this should be:
>
> * @avalon.meta.service type="net.osm.vault.Vault"
> * @avalon.meta.service type="net.osm.vault.KeystoreHandler:2.1.1"
Brain is on slow - what type?
>
>
> === comments ===
>
> Of course, the ":2.1.1" is there for a reason. The example implies
> this is not a freeform string, but rather a specific version of a
> service may be exported by providing ":2.1.1". Especially since no
> prefix results in auto-addition of ":1.0.0". This seems like a bad
> idea. These things are seperate and introducing another kind of
> construct (the ':' within a value to seperate values) seems much worse
> than
>
> * @avalon.meta.service type="net.osm.vault.Vault" version="1.1.1"
Moving to an explicit version would be implicit with migration to a
complete "common model".
> <type>
> <info>
> <version>5.1.0</version>
> <name>vault</name>
> </info>
> <services>
> <service type="net.osm.vault.Vault"
> version="1.1.1"/>
> </services>
> </type>
>
> Better stil, since the code will do
>
> class MyVault implements net.osm.vault.Vault
>
> it is probably a better idea to have the tool determine what version
> of Vault is exported by taking a look at what version of Vault is on
> the compile path. That requires some work of course.
More than a little work when you take into consideration the possibility
of multiple service defitions of the same type but with different
versions. This possibility exists as soon as you have stacked
repositories mapped to stacked classloaders.
> == @avalon.meta.stage ==
> class Lifecycle stage dependency declaration.
>
> === comments ===
>
> this is container-specific. Or perhaps excalibur-lifecycle-specific.
It is not container specific.
It is a concept used in the majority of Avalon containers (Fortress and
Merlin) and needs to be recognized by Phoenix because Phoenix needs to
throw out componets that require extension stages. This is one of those
"crunch" issues - if we don't recognize extensions we cannot deliver
portable defintions.
> == @avalon.meta.extension ==
> class Lifecycle stage handling capability declaration.
>
> === comments ===
>
> this is container-specific. Or perhaps excalibur-lifecycle-specific.
Unlike the stage declaration, this can be container specific because it
is the defintion of a components ability to provide extension handling.
>
> == @avalon.meta.logger ==
> enableLogging() Logging channel name declaration.
>
> /**
> * Supply of a logging channel to the component.
> * @param logger the logging channel
> * @avalon.meta.logger name="system"
> */
> public void enableLogging( Logger logger )
> {
> super.enableLogging( logger );
> m_system = logger.getChildLogger( "system" );
> }
>
> <type>
> <info>
> <version>2.4.0</version>
> <name>component</name>
> </info>
> <loggers>
> <logger name="system"/>
> </loggers>
> </type>
>
> === comments ===
>
> by contract, logger.getChildLogger() should work, even without such a
> declaration. So why is the declaration there at all?
The declaration is there so that assemblers can do useful things. You
want to declare (via a directive) a logging target for a nameed
channel. The only way you can do this from a management tool is to pull
in the channels declarations that a type declares. For example, you may
have a channel that you want to encrypt. How does the management tool
get a refewrence to the channel name in order to build the directive?
> In particular, what happens if you change to <logger name="blah"/>?
> Nothing, right?
Wrong.
What changes is the information that is presented to a management tool
and the potential for assembly stage customization and control (i.e.
what changes is the usability and manaagability of the component system).
>
>
>
> == @avalon.meta.context ==
> contextualize() Declaration of a specialized context class.
>
> /**
> * @avalon.meta.context type="net.osm.CustomContext"
> */
> public void contextualize( Context context )
> throws ContextException
> {
> CustomContext custom = (CustomContext) context;
> ...
> }
>
> === commments ===
>
> that should be discouraged! The contextualize() contract is that a
> component can expect to receive a Context, nothing /more/, nothing
> /less/. I think BlockContext shows how problematic the above is.
Avalon contextualize implies this constraint. In practive many
containers define extended context interfaces (Phoenix, Plexus, ...).
In addition users prefer to use things like File file =
context.getWorkingDirectory() as opposed to File file = (File)
context.get( SOME_KEY ). Thing is that this is required if you going to
run a Phoneix component that assumes BlockContext. The important point
here is that Avalon containers need to recognize this casting criteria
and reject the component if they cannot support it.
I.e. this is one of those CRUNCH issues.
>
> == @avalon.meta.entry ==
> contextualize() Context entry declaration.
>
> === comments ===
>
> thought for a bit how this might be combined with
> @avalon.meta.dependency. Probably not a good idea.
This is also one of those declarations that must be recognized.
Optional entries can be ignored by containers, non-option cannot be
ignored.
> == @avalon.meta.dependency ==
> service() Service type dependency declaration.
>
> === comments ===
>
> maps to '@avalon.dependency' in AMTAGS.
Yep.
> == More on versioning ==
>
> Lots of unanswered questions.
>
> - do you really need this granular versioning?
Yes.
> Isn't versioning per deployable unit enough?
No. Different granularity.
>
>
> - when is a service backwards compatible?
Backward compatibility is undefined at this time (i.e. versions are
managed at absolutes). Looking forward, backward compatability semantics
would require more work on things like the framework Version class.
> How do you specify a version range?
Version ranges are not included.
> - how are versions actually used in container space? What can you
> expect to happen when you declare a version?
Components in a hierachy can provide different versions of the same
interface. When your mapping a provider to a consumer component you
basically search up the tree for a component exporting a matching
service (where matching means equivalent interface classname and
equivalent version). As mentioned above - this matching algorith is
currtently absolute but this is isolated in the equality test for a
service reference (allowing more general version semantics in the future).
> - how "optional" can version support be in a container?
A container can ignore this information and depend on "hope". The level
of confidence you can assign to "hope" semantics is typically a
container implemetation issue. Containers that use this information can
provide assured solutions.
>
> == summary ==
>
> use of '@avalon.meta' is a bad idea. s/@avalon.meta/@merlin/ would be
> nice.
>
> My biggest issue is with versioning. Either go all the way and declare
> (for example) that the arguments to some of the tags are urns in the
> avalon namespace (and declare the format for urns in that namespace),
> or be consistent in application of a pattern.
>
> Also, I'm sceptical as to the need of this granular kind of
> versioning, especially with regard to implementations.
>
> === existing merlin tags ===
>
> @avalon.meta.namespace should just dissapear
+1
>
> @avalon.meta.version should just use '@version', if
> needed at all
-1 mixes different concerns
>
> @avalon.meta.attribute should just dissapear; this stuff
> is attributes already. If there's really a need for avalon container
> support, just implement a generic algorithm that stores all unknown
> attributes (or even all attributes), rather than invent a new
> mechanism
-1 should not dissapear - tag spec and structure can be improved
> @avalon.meta.name needs more specification as to intended
> usage, and perhaps should allow free-form strings
+1 on more specification
-1 on free-form strings
>
>
> @avalon.meta.lifestyle no comment
>
> @avalon.meta.service <-> AMTAGS @avalon.service; concerns
> about versioning setup
>
> @avalon.meta.stage tied to excalibur-lifecycle package
>
> @avalon.meta.extension tied to excalibur-lifecycle package
>
> @avalon.meta.logger should just dissapear
-1
Put on a suite, re-read email, and think about management implications.
>
> @avalon.meta.context has a use case because there exist
> components that depend on specific context implementations or
> extensions (ie BlockContext), but should be discouraged
If you have a complete interoperable solution - then there is no need to
discourage the practice. It is possible to provide solutions to both
context casting and context semantic additions (and both are required if
you want to provide complete Phoenix compatibility). All we should be
addressing here is "what information at the tag level is needed to
completely declare the criteria of a component type". Using Phoenix as
a case study this means (a) context casting assumptions, (b) context
enty assumptions, (c) context behavioural assumptions. Our tag model
for context has to address all three.
>
> @avalon.meta.entry no comment
>
> @avalon.meta.dependency <-> AMTAGS @avalon.service; concerns
> about versioning setup
>
>
> == conclusion ==
>
> The tags currently in use by merlin are not ready for generalization
> into a "full suite of tags". I think some things do not make sense,
> and in other places contracts are underspecified.
I think you have made some good suggestions for improving the Merlin tag
suite. I also think you still missing the point about what is really
required for a generic and complete specification that would enable
"assured component portability" and "assured component integrity". What
I'm talking about is recognizing that a bunch of stuff MUST BE
RECOGNIZED irrespective of container support. Until we reach a general
acceptance of this truth a common tag model will be of little value.
Cheers, Steve.
--
Stephen J. McConnell
mailto:mcconnell@apache.org
http://www.osm.net
Sent via James running under Merlin as an NT service.
http://avalon.apache.org/sandbox/merlin
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org