Hi

This mail is about the wiki http://wiki.apache.org/myfaces/Code_Generation:
An the topic on this wiki page
Generating base classes instead of templatesFinally I have found the reasons
about my previous suggestions, so I will proceed with the proper update of
the wiki. These are the changes:

*".......Note that (in a feature that may surprise some Java developers) it
appears quite valid for a class to have a package-scoped parent; the class
can still be subclassed or instantiated from outside the package. It
inherits public and protected members from its package-scoped parent which
can be called as normal. The only constraint is that it cannot be cast to
its parent type, as this is not accessible (although the Class object for
that type can still be obtained). It is not yet known whether inserting such
a package-scoped ancestor class into the ancestry of a component class in
the javax.faces package scope would be acceptable to the JSF TCK or not. If
the TCK accepts this, then the approach of generating a base class could
also be applied to myfaces core components......".*

In the practice the problem is that jsf core (myfaces and ri) uses
reflection to set the attributes and the following case fail:

//base component class from api
public class BaseComponent extends Object
{
    //......//
}

//package scope class
abstract class AbstractComponent extends BaseComponent
{
    private String value;

    public String getValue()
    {
        return value;
    }

    public void setValue(String value)
    {
        this.value = value;
    }
}

public class RealComponent extends AbstractComponent
{
    //......//
}

If an instance of RealComponent is created, you can access to the public api
defined on AbstractComponent. But if you use reflection to call
getValue or setValue using a code like this:

    public Object getValue(BaseComponent component){
        Object resp = null;
        try
        {
            BeanInfo beanInfo = null;
            try
            {
                beanInfo = Introspector.getBeanInfo(component.getClass());
            }
            catch (IntrospectionException e)
            {
                e.printStackTrace();
            }
            PropertyDescriptor[] propertyDescriptors = beanInfo
                    .getPropertyDescriptors();

            HashMap<String, PropertyDescriptor> _propertyDescriptorMap = new
HashMap<String, PropertyDescriptor>();
            for (int i = 0; i < propertyDescriptors.length; i++)
            {
                PropertyDescriptor propertyDescriptor =
propertyDescriptors[i];
                if (propertyDescriptor.getReadMethod() != null)
                {
                    _propertyDescriptorMap.put(propertyDescriptor.getName(),
                            propertyDescriptor);
                }
            }

            PropertyDescriptor pd = _propertyDescriptorMap.get("value");

            Method readMethod = pd.getReadMethod();

            Object[] EMPTY_ARGS = new Object[0];

            resp = readMethod.invoke(component, EMPTY_ARGS);

        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return resp;
    }

Fail with the following exception

java.lang.IllegalAccessException: Class javax.faces.component._Util can not
access a member of class org.apache.myfaces.test.AbstractComponent with
modifiers "public"
        at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at javax.faces.component._Util.getValue(_Util.java:54)
        at javax.faces.component.BaseComponent.getValueReflection(
BaseComponent.java:32)
        at javax.faces.other.ComponentTest.main(ComponentTest.java:14)

This behavior is a JDK bug:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4533479

One possible workaround is put the following line before invoke:

            readMethod.setAccessible(true);

Or make AbstractComponent public.

The conclusion is that the abstract base class should be public if and only
if it has in his body a attribute definition available on the tld. If the
abstract base class has some different code it can be package scope. This
behavior discard this approach for myfaces core api!

*".....Question: When there are multiple source trees for a module, does
maven build against them all simultaneously? That is, if a normal source
file references a source file in the generated-source tree which itself
references a source file in the normal source tree, does this work?..."
*
Response: One successful example is this hierarchy used for t:schedule

javax.faces.UIComponentBase
-----------myfaces core api
org.apache.myfaces.custom.schedule.AbstractUIScheduleBase    ----------- on
src/main/java
org.apache.myfaces.custom.schedule.UIScheduleBase
------------ generated on target/maven-faces-plugin/main/java
org.apache.myfaces.custom.schedule.UISchedule
------------ on src/main/java
org.apache.myfaces.custom.schedule.HtmlSchedule
------------ generated on target/maven-faces-plugin/main/java

COMMENT:
Generate in src/main/java or in target/maven-faces-plugin/main/java is
transparent for the IDE. If we generate in src/main/java, the generated code
will mix with hand written code, if we translate generated code to a
separate directory on src/main/java (for example src/main/java-generated)
technically it is equivalent to generate in
target/maven-faces-plugin/main/java (the difference is if we do mvn clean,
the code in target is deleted).

regards

Leonardo Uribe

Reply via email to