dev@woodstock.java.net

Re: Proposed Annotation Change

From: richard ratta <Richard.Ratta_at_Sun.COM>
Date: Tue, 04 Sep 2007 06:23:52 -0400

Back to the original issue ....

I don't think adding a "RendererClass" annotation on the component is
appropriate.
A Component does not determine its renderer. Its renderer is determined
by its
component family and renderer type.

I believe you can address your original issue by fixing the annotation
[rocessor to support
a many to one or none, relationship between component and renderer. That
funtionality
would help everyone.

....

As to the side discussion ....

I offered a suggestion to your simple use which you were using to
justify this change.
Your response should show you that using such "simple" use cases to
determine functionality
is not useful, because it is never the whole story, as we've seen that
story unfold.

I understood you to be trying to help the 80% of users that use your
templating system and
to help them not to have to write so many files. My suggestion of
template components rendering themselves does this and removes those
users from ever having
to even know about a renderer. It also does not prevent changing
renderkits, or substituting a
renderer for a given renderer-type and component family. That depends on
how you write the
rendering functionality. The standard JSF design pattern could be used
to just check for a
renderer for that component.

In the use case you are using, you say there are no renderers to
annotate so most of the
time there won't be any renderer and the simple user can be supported. A
user with more
complicated needs should have no problem editing faces-config.xml in
which case they
wouldn't use your new annotation because they would just annotate the
renderer.

....

-rick

Ken Paulsen wrote:

>
>
> richard ratta wrote:
>
>> The more I think about this the more confused I get as to why changing
>> a very specific annotation processor is the solution.
>>
>> JSF defines two strategies for rendering components. One is with a
>> renderkit
>> and the flexibility it offers . It defines a second strategy where
>> components render themselves.
>>
>> Your design appears to fall into the second strategy. In fact why
>> doesn's the template component superclass
>> implement the template rendering as well ?
>
> Why don't all the JSF standard components do it this way? Why don't
> any of the Woodstock components do it that way? The
> JSFTemplating-based Renderer have the SAME answer.
>
> There is NO difference between a Renderer that is template-based vs.
> one that is Java-based (except that there is not .java file for a
> template-based Renderer to annotate ;) ).
>
>> You don't need the a renderer at all as you claim, it just becomes
>> part of your
>> inherited component behavior.
>
> This reduces the options a component developer and the end users of
> the component have for Rendering a UIComponent. The would have
> hard-coded a 1-to-1 mapping between the output of the UIComponent and
> the UIComponent itself b/c there would be no mapping done at all.
>
> I do not want this. I want 1 UIComponent to be capable of being
> Rendered to N different devices / markup. I want the end-user to be
> able to override the default Renderer and provide their own
> implementation. Perhaps they'd extend the TemplateRenderer and add
> some monitoring / auditing features... perhaps they'd generate .java
> code from the template and use a .java based renderer which they've
> modified... perhaps they'd write their own alternate implementation of
> the Renderer. These are the SAME use-cases for which JSF provides the
> Renderer mappings.
>
>> We know that the current annotation processor supports components
>> that do not have a
>> a renderkit renderer. Unfortunately the processor reports warnings
>> because of this
>> but that is because of its specific nature.
>>
>> Just curious, what happens if a Woodstock component were to add the
>> @Renderer
>> annotation in error or is it appropriate in any way for the Woodstock
>> components ?
>
> The way I intended for this change to be used is to add the
> "rendererClass" attribute to a @Component annotation:
>
> @Component(type="com.sun.webui.jsf.DropDown",
> family="com.sun.webui.jsf.DropDown",
> rendererClass="com.sun.webui.jsf.renderkit.html.DropDownRenderer",
> ...)
>
> The above would work and you would not be required to annotate the
> DropDown Renderer. However... good practice would be to annotate it.
> And for cases where you have 1 UIComponent w/ many Renderers would not
> be satisified by this implementation.
>
> Adding the @Renderer annotation to a UIComponent class works provided
> that you use the rendererClass property as well... AND you don't have
> a @Component annotation. The way the current annotation processor is
> written, it doesn't support 2 annotations on a single class... or at
> least I haven't figured out the correct syntax to get that to work. I
> think the problem is that when you specify both annotations, the first
> annotation annotates the 2nd annotation instead of both annotating the
> class. Separating w/ commas didn't seem to help. There's a good
> change I'm doing something wrong when defining the annotation, though.
>
>> This really flies in the face of the strategy to have renderkits for
>> the Woodstock components
>> as well as JSF.
>
> ? I don't see how.
>
>> This makes me think even more so that if your components don't
>> utililize renderers in a
>> renderkit, then they are either components that are not rendered, or
>> they render themselves.
>
> They DO utilize this... but they do not DEFINE the Renderer in the
> Renderkit when using the template Renderer.
>
>
> Thanks!
>
> Ken
>
>>
>> -rick
>>
>> richard ratta wrote:
>>
>>>
>>>
>>> Ken Paulsen wrote:
>>>
>>>>
>>>>
>>>> richard ratta wrote:
>>>>
>>>>> I would like to see real requirements for this change, a little
>>>>> more on what
>>>>> is needed and why this change satisfies those requirements.
>>>>>
>>>>>> The motivation for this new feature is to be able to skip
>>>>>> creating a new Renderer for a component entirely. Jason Lee and
>>>>>> I are experimenting around with the Woodstock annotations +
>>>>>> JSFTemplating's TemplateRenderer to create components. I think
>>>>>> this is a good combination that reduces development to 2 files:
>>>>>> UIComponent file, and a template file. However, the current
>>>>>> annotations require a Renderer class to be present to place a
>>>>>> @Renderer annotation... and we don't have such a file in our
>>>>>> environment.
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> This rationalization really isn't very clear, technically speaking.
>>>>>
>>>>> I also don't see the relationship between a renderer-type and the
>>>>> renderer-class which is the relationship
>>>>> JSF defines. There is no relationship in JSF between a Component
>>>>> and a renderer-class. It is
>>>>>
>>>>> component-family
>>>>> renderer-type
>>>>> renderer-class
>>>>>
>>>>> And we have created complexities by dynamically changing a
>>>>> component's renderer-type
>>>>> at runtime based on the "type of request"; "normal" or "ajax".
>>>>>
>>>>
>>>> Hi Rick,
>>>>
>>>> Yes, JSF supports an N to N mapping between UIComponent and
>>>> Renderers. You can map N components to a single Renderer, or 1
>>>> UIComponent can end up being server by N different Renderers. In
>>>> order for this to work, as you pointed out, there cannot exist a
>>>> hard-coded relationship between the UIComponent class and the
>>>> Renderer class.
>>>>
>>>> For this reason this solution is not perfect... however, in my
>>>> scenario there is only 1 Java file, yet I'd like to take advantage
>>>> of the code generation done by the annotations so I can alleviate
>>>> developers of the need to write ANY xml in the faces-config.xml
>>>> file. I don't see any other practical way to do this w/ the
>>>> current Woodstock annotations / development environment.
>>>>
>>>> Also, if you look at the @annotations as the "default" generated
>>>> faces-config.xml file. Then this change *ONLY* provides a
>>>> *default* mapping between the UIComponent and 1 possible Renderer.
>>>> I see this as VERY valuable in my use case. Let me elaborate on
>>>> my use case....
>>>>
>>>> I have a template that might look something like:
>>>>
>>>> myComp.jsf:
>>>>
>>>> <span id="$this{componentId}"><b>$property{value}</b></span>
>>>>
>>>> Ok... that's a VERY simple component that wraps the "value"
>>>> property of the component in a span w/ bold tags... but valid...
>>>> see my TSS article for a much more complicated example
>>>> (http://www.theserverside.com/tt/articles/article.tss?l=JSFTemplateComponent).
>>>>
>>>>
>>>> I then have a UIComponent that is something like:
>>>>
>>>> public class MyComp extends TemplateInsertComponentBase implements
>>>> NamingContainer {
>>>> public MyComp() {
>>>> super();
>>>> setRendererType("com.sun.MyComp");
>>>> setLayoutDefinitionKey("jsftemplating/myComp.jsf");
>>>> }
>>>>
>>>> public String getFamily() {
>>>> return "com.sun.MyComp";
>>>> }
>>>> }
>>>>
>>>> I don't have ANY other files for the component... however, I would
>>>> like to use Woodstock annotations to further simplify this so that
>>>> I don't have to configure the *faces-config.xml* file or write a
>>>> *JSP tag class*. With the current annotations I can't quite do
>>>> this because I do NOT have a Renderer class
>>>
>>>
>>>
>>> then what is
>>> 'rendererClass="com.sun.jsftemplating.renderer.TemplateRenderer"' ?
>>> Why not just annotate that renderer, to support the family,
>>> "com.sun.Template" with renderer
>>> type "com.sun.Template", and have anyone that wants to use you
>>> "templates" make their component
>>> belong to that family ?
>>>
>>> Currently the annotation processor does enforce a 1 to 1 mapping and
>>> that is what I would rather
>>> see fixed so that it supports the JSF mapping possibilities.
>>>
>>> Adding other arbitraty rules and annotations just makes the
>>> annotations more cofusing.
>>> If they followed the JSF spec with respect to mapping components to
>>> renderers then
>>> it is easier to understand since it follows a standard specification.
>>> That should solve your use case as well, no ?
>>>
>>> I'm surprised everyone wants to get away from faces-config.xml. It
>>> seemed pretty useful to
>>> me to be able to map and define meta-data for compoents,
>>> unfortunately we didn't conform to
>>> it more strictly.
>>>
>>>
>>> -rick
>>>
>>>> where I can put the @Renderer annotation. With my proposed change,
>>>> I can add
>>>> *rendererClass="com.sun.jsftemplating.renderer.TemplateRenderer"*
>>>> to the @Component annotation and everything works.
>>>>
>>>> Does this make more sense?
>>>>
>>>> If we can do this... I'd like to write a java.sun.com article
>>>> explaining how this is done. My TSS article has already generated
>>>> a lot of interest, I think combining this with the Woodstock
>>>> annotations offers a solution that a lot of people would be
>>>> interested in.
>>>>
>>>>> See more inline
>>>>>
>>>>
>>>> ... (see below)
>>>>
>>>>>
>>>>> - RendersInfo(Map annotationValueMap) {
>>>>> + RendersInfo(Map annotationValueMap, String className) {
>>>>> this.annotationValueMap = annotationValueMap;
>>>>> + this.className = className;
>>>>> }
>>>>>
>>>>> You are changing an existing interface here.
>>>>> Any existing code that relies on the previous interface will break.
>>>>> You have to support the original interface as well as the new one.
>>>>
>>>>
>>>>
>>>>
>>>> Yes, you are correct. However, this is a class that is used
>>>> (indirectly) by APT only during compile time and doesn't manifest
>>>> itself at all during runtime (afaik). So unless someone has
>>>> extended the framework Woodstock has to develop their own JSF
>>>> annotations AND directly instantiates this static inner class
>>>> (DeclaredRendererInfo.RendersInfo), then this isn't a problem.
>>>>
>>>> Nevertheless, I'd be happy to support the old single-argument
>>>> constructor as well since this is easy to do and good practice.
>>>>
>>>> Ken
>>>>
>>>>>
>>>>> -rick
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>> Ken Paulsen wrote:
>>>>>
>>>>>>
>>>>>> I would like to commit a minor change to the annotations
>>>>>> woodstock provides. The change allows a @Component annotation to
>>>>>> take a "rendererClass" attribute to specify a Renderer that
>>>>>> should be used to Renderer the UIComponent.
>>>>>>
>>>>>> The motivation for this new feature is to be able to skip
>>>>>> creating a new Renderer for a component entirely. Jason Lee and
>>>>>> I are experimenting around with the Woodstock annotations +
>>>>>> JSFTemplating's TemplateRenderer to create components. I think
>>>>>> this is a good combination that reduces development to 2 files:
>>>>>> UIComponent file, and a template file. However, the current
>>>>>> annotations require a Renderer class to be present to place a
>>>>>> @Renderer annotation... and we don't have such a file in our
>>>>>> environment.
>>>>>>
>>>>>> I have run the annotation processor before and after this change
>>>>>> and compared the results. There is no difference at all (as
>>>>>> there shouldn't be since it only adds an optional property which
>>>>>> isn't currently present on any Woodstock components).
>>>>>>
>>>>>> The diffs are attached. Please let me know if you have any
>>>>>> objections to me checking in this change. Or if I should check
>>>>>> it in somewhere other than the HEAD.
>>>>>>
>>>>>> Thanks!
>>>>>>
>>>>>> Ken
>>>>>>
>>>>>> woodstock/annotations> cvs -q diff -u
>>>>>> Index: library/src/com/sun/faces/annotation/Component.java
>>>>>> ===================================================================
>>>>>> RCS file:
>>>>>> /cvs/woodstock/annotations/library/src/com/sun/faces/annotation/Component.java,v
>>>>>>
>>>>>> retrieving revision 1.1
>>>>>> diff -u -r1.1 Component.java
>>>>>> --- library/src/com/sun/faces/annotation/Component.java 15 Feb
>>>>>> 2007 21:47:16 -0000 1.1
>>>>>> +++ library/src/com/sun/faces/annotation/Component.java 31 Aug
>>>>>> 2007 21:28:05 -0000
>>>>>> @@ -134,6 +134,8 @@
>>>>>> * is set to false), then providing a value for this element
>>>>>> has no effect.
>>>>>> */
>>>>>> public String tagRendererType() default "";
>>>>>> +
>>>>>> + public String rendererClass() default "";
>>>>>>
>>>>>> /**
>>>>>> * An optional short description of this component, typically
>>>>>> used as a tool
>>>>>> Index: library/src/com/sun/faces/annotation/Renderer.java
>>>>>> ===================================================================
>>>>>> RCS file:
>>>>>> /cvs/woodstock/annotations/library/src/com/sun/faces/annotation/Renderer.java,v
>>>>>>
>>>>>> retrieving revision 1.1
>>>>>> diff -u -r1.1 Renderer.java
>>>>>> --- library/src/com/sun/faces/annotation/Renderer.java 15 Feb
>>>>>> 2007 21:48:01 -0000 1.1
>>>>>> +++ library/src/com/sun/faces/annotation/Renderer.java 31 Aug
>>>>>> 2007 21:28:05 -0000
>>>>>> @@ -82,6 +82,8 @@
>>>>>> @Target(ElementType.ANNOTATION_TYPE)
>>>>>> public @interface Renders {
>>>>>>
>>>>>> + public String rendererClass() default "";
>>>>>> +
>>>>>> /**
>>>>>> * The renderer type for this component and renderer
>>>>>> combination. If
>>>>>> * this annotation contains a single component family,
>>>>>> and a renderer type
>>>>>> Index: processor/src/com/sun/faces/mirror/DeclaredRendererInfo.java
>>>>>> ===================================================================
>>>>>> RCS file:
>>>>>> /cvs/woodstock/annotations/processor/src/com/sun/faces/mirror/DeclaredRendererInfo.java,v
>>>>>>
>>>>>> retrieving revision 1.1
>>>>>> diff -u -r1.1 DeclaredRendererInfo.java
>>>>>> ---
>>>>>> processor/src/com/sun/faces/mirror/DeclaredRendererInfo.java
>>>>>> 15 Feb 2007 21:56:49 -0000 1.1
>>>>>> +++
>>>>>> processor/src/com/sun/faces/mirror/DeclaredRendererInfo.java
>>>>>> 31 Aug 2007 21:28:06 -0000
>>>>>> @@ -44,9 +44,10 @@
>>>>>> this.annotationValueMap = annotationValueMap;
>>>>>> renderings = new ArrayList<RendersInfo>();
>>>>>> if (this.annotationValueMap.containsKey(VALUE)) {
>>>>>> + String qn = decl.getQualifiedName();
>>>>>> for (Object value : (List)
>>>>>> this.annotationValueMap.get(VALUE)) {
>>>>>> Map nestedAnnotationValueMap = (Map) value;
>>>>>> - renderings.add(new
>>>>>> RendersInfo(nestedAnnotationValueMap));
>>>>>> + renderings.add(new
>>>>>> RendersInfo(nestedAnnotationValueMap, qn));
>>>>>> }
>>>>>> }
>>>>>> }
>>>>>> @@ -60,15 +61,28 @@
>>>>>> */
>>>>>> static public class RendersInfo {
>>>>>>
>>>>>> + static String RENDERER_CLASS = "rendererClass";
>>>>>> static String RENDERER_TYPE = "rendererType";
>>>>>> static String COMPONENT_FAMILY = "componentFamily";
>>>>>>
>>>>>> Map annotationValueMap;
>>>>>> + String className;
>>>>>>
>>>>>
>>>>>> + /**
>>>>>> + * The renderer class (the qualified name by default).
>>>>>> + */
>>>>>> + public String getRendererClass() {
>>>>>> + if
>>>>>> (this.annotationValueMap.containsKey(RENDERER_CLASS)) {
>>>>>> + return (String)
>>>>>> this.annotationValueMap.get(RENDERER_CLASS);
>>>>>> + }
>>>>>> + return this.className;
>>>>>> + }
>>>>>> +
>>>>>> /**
>>>>>> * The renderer type.
>>>>>> */
>>>>>> Index:
>>>>>> processor/src/com/sun/faces/mirror/FacesAnnotationProcessor.java
>>>>>> ===================================================================
>>>>>> RCS file:
>>>>>> /cvs/woodstock/annotations/processor/src/com/sun/faces/mirror/FacesAnnotationProcessor.java,v
>>>>>>
>>>>>> retrieving revision 1.2
>>>>>> diff -u -r1.2 FacesAnnotationProcessor.java
>>>>>> ---
>>>>>> processor/src/com/sun/faces/mirror/FacesAnnotationProcessor.java
>>>>>> 9 Jun 2007 13:35:41 -0000 1.2
>>>>>> +++
>>>>>> processor/src/com/sun/faces/mirror/FacesAnnotationProcessor.java
>>>>>> 31 Aug 2007 21:28:08 -0000
>>>>>> @@ -197,6 +197,30 @@
>>>>>>
>>>>>> this.declaredComponentSet.add(componentInfo);
>>>>>>
>>>>>> this.declaredClassMap.put(typeDecl.getQualifiedName(),
>>>>>> componentInfo);
>>>>>> typeInfo = componentInfo;
>>>>>> + // Check to see if this annotation also
>>>>>> specifies a Renderer
>>>>>> + Object rendererClass =
>>>>>> annotationValueMap.get("rendererClass");
>>>>>> + if ((rendererClass != null)
>>>>>> + &&
>>>>>> !rendererClass.toString().equals("")) {
>>>>>> + // Create the "Renders" Map
>>>>>> + Map renders = new HashMap();
>>>>>> + renders.put("rendererClass",
>>>>>> rendererClass);
>>>>>> + Object type =
>>>>>> annotationValueMap.get("tagRendererType");
>>>>>> + if ((type == null) ||
>>>>>> type.toString().equals("")) {
>>>>>> + type =
>>>>>> annotationValueMap.get("type");
>>>>>> + }
>>>>>> + renders.put("rendererType", type);
>>>>>> + ArrayList families = new ArrayList();
>>>>>> +
>>>>>> families.add(annotationValueMap.get("family"));
>>>>>> + renders.put("componentFamily",
>>>>>> families);
>>>>>> +
>>>>>> + List list = new ArrayList();
>>>>>> + list.add(renders);
>>>>>> + Map rendererMap = new HashMap();
>>>>>> + rendererMap.put("value", list);
>>>>>> + DeclaredRendererInfo rendererInfo =
>>>>>> + new
>>>>>> DeclaredRendererInfo(rendererMap, (ClassDeclaration) typeDecl);
>>>>>> +
>>>>>> this.declaredRendererSet.add(rendererInfo);
>>>>>> + }
>>>>>> } else if
>>>>>> (typeDecl.getAnnotation(Renderer.class) != null) {
>>>>>> // This is a renderer class
>>>>>> Map<String,Object> annotationValueMap =
>>>>>> Index:
>>>>>> processor/src/com/sun/faces/mirror/generator/FacesConfig.template
>>>>>> ===================================================================
>>>>>> RCS file:
>>>>>> /cvs/woodstock/annotations/processor/src/com/sun/faces/mirror/generator/FacesConfig.template,v
>>>>>>
>>>>>> retrieving revision 1.2
>>>>>> diff -u -r1.2 FacesConfig.template
>>>>>> ---
>>>>>> processor/src/com/sun/faces/mirror/generator/FacesConfig.template
>>>>>> 17 Feb 2007 23:50:09 -0000 1.2
>>>>>> +++
>>>>>> processor/src/com/sun/faces/mirror/generator/FacesConfig.template
>>>>>> 31 Aug 2007 21:28:08 -0000
>>>>>> @@ -52,7 +52,7 @@
>>>>>> <renderer>
>>>>>> <component-family>${componentFamily}</component-family>
>>>>>> <renderer-type>${rendering.rendererType}</renderer-type>
>>>>>> -
>>>>>> <renderer-class>${rendererInfo.qualifiedName}</renderer-class>
>>>>>> +
>>>>>> <renderer-class>${rendering.rendererClass}</renderer-class>
>>>>>> </renderer>
>>>>>> #end
>>>>>> #end
>>>>>>
>>>>>> ---------------------------------------------------------------------
>>>>>>
>>>>>> To unsubscribe, e-mail: dev-unsubscribe_at_woodstock.dev.java.net
>>>>>> For additional commands, e-mail: dev-help_at_woodstock.dev.java.net
>>>>>>
>>>>>
>>>>> ---------------------------------------------------------------------
>>>>> To unsubscribe, e-mail: dev-unsubscribe_at_woodstock.dev.java.net
>>>>> For additional commands, e-mail: dev-help_at_woodstock.dev.java.net
>>>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: dev-unsubscribe_at_woodstock.dev.java.net
>>> For additional commands, e-mail: dev-help_at_woodstock.dev.java.net
>>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe_at_woodstock.dev.java.net
>> For additional commands, e-mail: dev-help_at_woodstock.dev.java.net
>>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe_at_woodstock.dev.java.net
> For additional commands, e-mail: dev-help_at_woodstock.dev.java.net
>