Hi All,

As you've seen, I've committed some code to a branch for people to look
at. It's really rough at the moment, but enough is there to try out and
hopefully determine whether this approach is reasonable or not.

I won't be able to do any more on this for the next few days due to the
JSFDays08 conference, so am just dumping this now for your
consideration..


To try it out:

svn co
https://svn.apache.org/repos/asf/myfaces/myfaces-build-tools/branches/skitching

cd myfaces-builder-plugin
mvn install
cd ../api
mvn compile

This will generate some output like:
--dumping artifacts--
==Component
class:UIData
type:javax.faces.Data
....
--dumped artifacts--
[INFO]
Generating 
/home/simon/apache/myfaces/myfaces-build-tools/maven2-plugins/skitching/api/target/META-INF/faces-config.xml
[INFO]
------------------------------------------------------------------------
[ERROR] BUILD ERROR


The build error is deliberate; I currently throw an exception in the
plugin after running all the bits that I have actually implemented, as
there is never any point in continuing beyond that.

Points to note:

(1)
The code is based on myfaces-faces-plugin, but vastly simplified. I see
no reason why all the features I've removed cannot be added back in
later, but don't want to deal with the full complexity at the moment.

(2)
The most important part is how Model objects are built up. A Model
contains info about ConverterModels, ValidatorModels, ComponentModels,
etc.

This code uses qdox to look for special javadoc tags, but the concept of
a Model is independent. There is no reason why the Model objects cannot
also be populated by parsing xml files. In future, therefore, we could
merge data from multiple sources. That's why QdoxModelBuilder implements
the ModelBuilder interface.

(3)
This demo is creating a faces-config file for the api project. Of course
that doesn't occur in practice; we create the config file only for the
impl project. In a real situation I expect to run this goal only in the
impl project, and add a dependency from impl on api-source then unpack
the api source so it can be scanned at the same time. But that's just
details. The point here is that we can scan source...

(4)
The comment tags added are really simple to work with:

 *
 * @mfp.component
 *   family="javax.faces.UIData"

Note that where possible, "conventions" are used to populate the model:

 String componentTypeDflt = null;
 JavaField fieldComponentType = clazz.getFieldByName("COMPONENT_TYPE");
 if (fieldComponentType != null)
 {
  componentTypeDflt =
    clean(fieldComponentType.getInitializationExpression());
 }

Here the QdoxModelBuilder is looking for our conventional static field.
Only if this is missing is a "type" property for the @mfp.component
annotation required.

(5)
Error handling is nice. If you leave out a mandatory tag-property, the
plugin will tell you the exact class and line.

(6)
Properties on components are determined by scanning the component class
*and its parent classes/interfaces* (not yet tested). This is much more
elegant than having xml files duplicate information about the class
hierarchies.

Note that the code does not try to represent Model data about each class
and chain them together as the myfaces-faces-plugin does. Instead when
an @mfp.component class is found, it just scans the ancestry and
directly adds all the props to the model for that component class. This
seems simpler; however we could do it in the myfaces-faces-plugin way if
necessary.

(7)
Currently, faces-config.xml is generated from code. As noted in that
code, it would probably be better to use Velocity templates or xslt.

As you can see from the stuff written to stdout, there is more metadata
gathered by the plugin than is currently written to the faces-config.xml
output.

(8)
The model description and longDescription fields are (or will be)
populated from the javadoc comments on the classes. This removes the
current ugly duplication of this information. There is therefore no
danger of inconsistency between what a taglib says, what a tag class
says, and what the component class javadoc says.

If there is a need to have extra info in the class javadoc that is not
in the tag desc fields, then an @mfp.endDesc annotation could be added
that can be inserted into the middle of the javadoc to separate the bits
that should go into tags from the bits that belong only in the class.

(9)
This approach is perfectly compatible with code-generation for
uicomponent classes. When a class with @mfp.component is discovered
which has an abstract qualifier on the class then that info can be added
to the model, and the code-generation pass can detect that and create
the subclass for it. To handle properties, the abstract base component
class just need to declare abstract methods with the necessary
annotations; that info is then available to the code-generation phase.

(10)
No separate "build" project is required.


I think this is looking very promising, and appears *much* easier to
maintain than dozens of xml metadata files. I think 3 of 4 days of work
on this plugin would be enough to get it to a state where it could be
used to generate core-1.1, core-1.2 and tomahawk. Adding in all the
trinidad-specific features looks like significantly more work though.

And it is quite similar to the existing Tobago approach, so maybe the
plugin could also be enhanced to allow Tobago to use it too - if they
want to.


Regards,
Simon

On Mon, 2008-03-10 at 22:16 +0100, simon wrote:
> Hi Matthias,
> 
> Thanks for reminding me about Tobago. I did mean to have a look at their
> configuration-handling, but had not got around to it. I have spent the
> last hour or so looking at it, and here's a brief summary.
> 
> The tobago-tool-annotation module defines a bunch of source-retention
> annotation classes. The tobago-tool-apt module then defines a library
> that uses the com.sun.mirror.apt apis to define annotation-processor
> classes. And the myfaces-apt-plugin module then defines a maven2 plugin
> that builds a commandline and execs the external sun "apt" commandline
> tool to apply the annotation-processor classes to the source.
> 
> The result is of course that the faces-config.xml and
> facelets.tabglib.xml files can be generated from the source. This is
> quite similar to what I am proposing.
> 
> Note that Tobago does write tag classes "by hand" rather than generate
> them; this does mean it needs a bit less metadata than we do (I think
> we're all agreed on generating tag classes at least).
> 
> I did consider using apt earlier, and decided not to. The main reasons
> are:
> (a) requires the sun apt tool to be on the build machine
> (b) requires executing an external process (ecch)
> (c) does not provide access to the javadoc attached to classes, methods
> and fields.
> 
> In addition, the .class files must be 1.5-or-later format. I think
> Tobago uses retroweaver to then make them 1.4-compatible.
> 
> Using QDOX and "doclet annotations" rather than real annotations seems
> nicer here. IDE tool support for the annotations is lost, but in all
> other ways it seems to work out better. It's a shame that
> source-retention annotations aren't supported better; they *should* be
> able to do all that qdox can, and *should* have a standard api to handle
> them. Hopefully one day a JCP will fix this, but for the moment, they
> just suck.
> 
> So in short: what I'm proposing is about half-way between what Tobago
> does (annotation-driven) and what the myfaces-maven-plugin does (richer
> metadata to allow code generation).
> 
> Regards,
> Simon
> 
> On Mon, 2008-03-10 at 09:04 +0100, Matthias Wessendorf wrote:
> > Hi,
> > 
> > On Mon, Mar 10, 2008 at 8:54 AM, [EMAIL PROTECTED]
> > <[EMAIL PROTECTED]> wrote:
> > > Yes, long descriptions are supported; see the example output I showed in
> > >  the original email. And I see no reason why every option currently
> > >  offered in xml cannot be supported via either an annotation or even
> > >  better automatically by introspecting the classes.
> > 
> > have you also looked at Tobago?
> > They currently use @nnotations to (at least) generate the required
> > XML for faces-config / facelets
> > 
> > -Matthias
> > 
> > >
> > >  I did want to build the annotation-handling functionality into the
> > >  existing plugin so that the two sources of info (xml config files and
> > >  doc-annotations) could be merged together. However the existing
> > >  myfaces-faces-plugin code is not well structured for that purpose; it
> > >  makes a lot of very xml-specific assumptions rather than having a
> > >  neutral "metadata" representation. I see no reason why the two couldn't
> > >  co-exist eventually, but intend to build a separate plugin first.
> > >
> > >  There is no reason why both plugins cannot be run separately, ie the
> > >  pom.xml be configured to run both. But that does lead to a lot of
> > >  inconsistency and redundancy; for example, one component will use the
> > >  annotations on a base class to determine settings for inherited
> > >  properties while another will ignore this annotation info completely and
> > >  get its data from an xml config file that contains (hopefully) the same
> > >  data but in another format. Ecch. I would therefore suggest not having
> > >  some components configured via xml and others via annotations in the
> > >  same project.
> > >
> > >  What I would like to see is core-1.1 and tomahawk use the approach I'm
> > >  suggesting here. All that is needed is to add annotations to the
> > >  existing code and move the comments that are currently in the tld-files
> > >  into javadoc comments on the appropriate fields. Optionally the property
> > >  methods can be reduced to abstract methods and the save/restore methods
> > >  removed if the generate-component-class approach is wanted.
> > >
> > >  With this approach it is possible to generate a concrete subclass with
> > >  property implementations and saveState/restoreState methods as has been
> > >  discussed earlier. I'm still not convinced about this, but can live with
> > >  it. Using annotations instead of lots of xml config files is a different
> > >  issue.
> > >
> > >  Regards,
> > >  Simon
> > >
> > >
> > >
> > >  Martin Marinschek schrieb:
> > >
> > >
> > > > Sounds interesting. Will you support everything the XML-syntax allows
> > >  > to supply now? E.g, long descriptions?
> > >  >
> > >  > will it basically be an option which component-set wants to use which
> > >  > frontend? Then slowly every component set could decide if it wants to
> > >  > move over...
> > >  >
> > >  > How about restoreState/saveState? the getter?
> > >  >
> > >  > regards,
> > >  >
> > >  > Martin
> > >  >
> > >  > On Sun, Mar 9, 2008 at 10:20 PM, simon <[EMAIL PROTECTED]> wrote:
> > >  >
> > >  >> Hi All,
> > >  >>
> > >  >>  Currently, trinidad and core-1.2 use the myfaces-faces-plugin to
> > >  >>  generate tag classes, config files and component classes.
> > >  >>
> > >  >>  There is some work going on to use this for core-1.1 and tomahawk 
> > > too.
> > >  >>
> > >  >>  As I mentioned earlier, I don't like the approach used by the current
> > >  >>  myaces-faces-plugin; I think the large number of xml config files 
> > > that
> > >  >>  are needed is not elegant or user-friendly. I proposed using some 
> > > kind
> > >  >>  of "annotation" in the source to drive this process instead.
> > >  >>
> > >  >>  I have been working on this recently, and have got the first steps
> > >  >>  running. It's still very rough so I won't post the code, but wanted 
> > > to
> > >  >>  let you know what I'm working on. All feedback is welcome.
> > >  >>
> > >  >>  Here's an instrumented UIData class from core-1.1:
> > >  >>
> > >  >>
> > >  >>  /**
> > >  >>   * Represents a component which has multiple "rows" of data.
> > >  >>   * <p>
> > >  >>   * The children of this component are expected to be
> > >  >>  ...
> > >  >>   *
> > >  >>   * @mfp.component
> > >  >>   *   type = "javax.faces.Data"
> > >  >>   *   family = "javax.faces.Data"
> > >  >>   *   defaultRendererType = "javax.faces.Table"
> > >  >>   *   desc="tabular data"
> > >  >>   */
> > >  >>  public class UIData extends UIComponentBase implements 
> > > NamingContainer
> > >  >>  {
> > >  >>   ...
> > >  >>     /**
> > >  >>      * Set the name of the temporary variable that will be exposed to
> > >  >>      * child components of the table to tell them what the "rowData"
> > >  >>      * object for the current row is. This value must be a literal
> > >  >>      * string (EL expression not permitted).
> > >  >>      *
> > >  >>      * @mfp.property literalOnly=true
> > >  >>      */
> > >  >>     public void setVar(String var)
> > >  >>     {
> > >  >>         _var = var;
> > >  >>     }
> > >  >>  }
> > >  >>
> > >  >>  The code I've got so far just scans the source code to build up a
> > >  >>  "meta-data structure" holding the relevant data. This would then be 
> > > used
> > >  >>  to drive the file-generation step, hopefully reusing the existing 
> > > code
> > >  >>  from the myfaces-faces-plugin. In other words, I'm proposing 
> > > replacing
> > >  >>  the "front end" of the plugin but not the back-end.
> > >  >>
> > >  >>  Dumping the parsed data gives:
> > >  >>
> > >  >>  --dumping artifacts--
> > >  >>  == Component
> > >  >>  class:UIData
> > >  >>  type:javax.faces.Data
> > >  >>  prop:setVar
> > >  >>   class:java.lang.String
> > >  >>   isLiteral:true
> > >  >>   desc:Set the name of the temporary variable that will be exposed to
> > >  >>  child components of the table to tell them what the "rowData"
> > >  >>  object for the current row is. This value must be a literal
> > >  >>  string (EL expression not permitted).
> > >  >>  --dumped artifacts--
> > >  >>
> > >  >>
> > >  >>  Note that the javadoc comments from the class are taken into the
> > >  >>  metadata. So comments in the component, the generated tag-class and 
> > > the
> > >  >>  taglib file are automatically identical and are maintained in the
> > >  >>  *normal* manner for java developers.
> > >  >>
> > >  >>  Also, the property type is automatically inferred from the method
> > >  >>  declaration, rather than needing to be specified in xml. By the way, 
> > > the
> > >  >>  fact that we need a method to attach the @mfp marker to does not mean
> > >  >>  that we need an implementation; an abstract method would be fine if
> > >  >>  code-generation is being used to then create a concrete subclass.
> > >  >>
> > >  >>  Possibly some of those attributes on the @mfp.component tag could be
> > >  >>  made optional by applying standard patterns, eg looking for a public
> > >  >>  static field with name "COMPONENT_TYPE" on the class.
> > >  >>
> > >  >>
> > >  >>  Notes:
> > >  >>
> > >  >>  * The code uses the codehaus QDOX library.
> > >  >>  * java15 annotations are not used because
> > >  >>   (a) we want to support 1.4
> > >  >>   (b) java15 compile-retention annotations are not nice to work with
> > >  >>   (c) we explicitly want the javadoc comments
> > >  >>
> > >  >>
> > >  >>  Regards,
> > >  >>  Simon
> > >  >>
> > >  >>
> > >  >>
> > >  >
> > >  >
> > >  >
> > >  >
> > >
> > >
> > 
> > 
> > 
> 

Reply via email to