You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@myfaces.apache.org by "Simon Kitching (JIRA)" <de...@myfaces.apache.org> on 2005/11/22 03:05:41 UTC

[jira] Created: (MYFACES-856) t:aliasBean + binding + panelGrid causes exception

t:aliasBean + binding + panelGrid causes exception
--------------------------------------------------

         Key: MYFACES-856
         URL: http://issues.apache.org/jira/browse/MYFACES-856
     Project: MyFaces
        Type: Bug
    Reporter: Simon Kitching


The following use of t:aliasBean causes an exception:

==== including page

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>

<f:view>
  <h:panelGrid>
    <t:aliasBean alias="#{target}" value="#{someBean}">
      <jsp:include page="included.jsp"/>
    </t:aliasBean>
  </h:panelGrid>
</f:view>

==== included page

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>

<f:subview id="foo">
    <h:inputText binding="#{target.input}"/>
</f:subview>


The thrown exception is:

javax.faces.el.PropertyNotFoundException: Base is null: target
        at org.apache.myfaces.el.ValueBindingImpl.resolveToBaseAndProperty(ValueBindingImpl.java:457)
        at org.apache.myfaces.el.ValueBindingImpl.setValue(ValueBindingImpl.java:245)
        at org.apache.myfaces.application.ApplicationImpl.createComponent(ApplicationImpl.java:434)


I believe the problem is that panelGrid renders its children. This means that the components are first created, then the panelGrid iterates over them to render them. However the alias bean hasn't set up the alias at the time the components are being created. And ApplicationImpl.createComponent(binding, context, componentType) is immediately evaluating the binding expression, resulting in the target bean for the binding not being found.

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira


[jira] Commented: (MYFACES-856) t:aliasBean + binding + panelGrid causes exception

Posted by "Simon Kitching (JIRA)" <de...@myfaces.apache.org>.
    [ http://issues.apache.org/jira/browse/MYFACES-856?page=comments#action_12358326 ] 

Simon Kitching commented on MYFACES-856:
----------------------------------------

Sigh. It looks like AliasBean just won't work with component bindings. At least I can't see any way to do it.

The issue with AliasBean when the view is being created is solvable by adding doStartTag/doEndTag methods to AliasBeanTag.

However when restoring an existing view, the bindings are executed by LifecycleImpl.recursivelyHandleComponentReferencesAndSetValid. And this method doesn't delegate the operation to the actual component, but instead breaks encapsulation by evaluating the component's binding itself. This means there is no way to execute "before" and "after" operations around the rebinding of the child components.

I've therefore just added a comment on AliasBean.java stating that bindings are not supported. I will update the wiki too.

Well, there *might* be a way to do it, by having AliasBean create a dummy first and last child, and catch calls to getValueBinding("binding") on them or some such nasty hack. I don't think that's worth doing though.

Mario's approach above does work, but by effectively promoting all AliasBean so that their alias is visible over the whole view tree. That's a fair solution for specific sites but I don't think it's a good general solution. For a start, it makes it impossible to reuse the same block of components twice in the same page bound to different real beans. And anyway as far as I can see, it's just equivalent to putting the aliasbean directly after the f:view and enclosing the entire page in it. Mario, have I missed something?

I'll leave this issue open for a few days just in case someone smarter than me comes up with a proper solution.

> t:aliasBean + binding + panelGrid causes exception
> --------------------------------------------------
>
>          Key: MYFACES-856
>          URL: http://issues.apache.org/jira/browse/MYFACES-856
>      Project: MyFaces
>         Type: Bug
>     Reporter: Simon Kitching

>
> The following use of t:aliasBean causes an exception:
> ==== including page
> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
> <f:view>
>   <h:panelGrid>
>     <t:aliasBean alias="#{target}" value="#{someBean}">
>       <jsp:include page="included.jsp"/>
>     </t:aliasBean>
>   </h:panelGrid>
> </f:view>
> ==== included page
> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
> <f:subview id="foo">
>     <h:inputText binding="#{target.input}"/>
> </f:subview>
> The thrown exception is:
> javax.faces.el.PropertyNotFoundException: Base is null: target
>         at org.apache.myfaces.el.ValueBindingImpl.resolveToBaseAndProperty(ValueBindingImpl.java:457)
>         at org.apache.myfaces.el.ValueBindingImpl.setValue(ValueBindingImpl.java:245)
>         at org.apache.myfaces.application.ApplicationImpl.createComponent(ApplicationImpl.java:434)
> I believe the problem is that panelGrid renders its children. This means that the components are first created, then the panelGrid iterates over them to render them. However the alias bean hasn't set up the alias at the time the components are being created. And ApplicationImpl.createComponent(binding, context, componentType) is immediately evaluating the binding expression, resulting in the target bean for the binding not being found.

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira


[jira] Commented: (MYFACES-856) t:aliasBean + binding + panelGrid causes exception

Posted by "Mario Ivankovits (JIRA)" <de...@myfaces.apache.org>.
    [ http://issues.apache.org/jira/browse/MYFACES-856?page=comments#action_12358218 ] 

Mario Ivankovits commented on MYFACES-856:
------------------------------------------

Hi Simon,


I use the following HACK to create the alias right after the view is restored:
We also use <x:aliasBeansScope>, dont know if this make a difference for this "solution"


import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Locale;

import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;

import org.apache.commons.logging.Log;
import org.apache.myfaces.custom.aliasbean.AliasBean;

import com.ops.OPSJ.lib.Application;
import com.ops.OPSJ.lib.ApplicationObjectInterface;
import com.ops.OPSJ.lib.OLog;

public class ViewHandler extends javax.faces.application.ViewHandler
{
	private final javax.faces.application.ViewHandler original;
	
	public ViewHandler(javax.faces.application.ViewHandler original)
	{
		this.original = original;
	}

	public UIViewRoot restoreView(FacesContext facesContext, String viewId)
	{
		UIViewRoot root = original.restoreView(facesContext, viewId);
		if (root != null)
		{
			processAliases(facesContext, root);
		}
		return root;
	}

	protected void processAliases(FacesContext context, UIComponent root)
	{
		if (root == null)
		{
			return;
		}

		for (Iterator it = root.getFacetsAndChildren(); it.hasNext(); )
		{
			UIComponent component = (UIComponent)it.next();

			if (component instanceof AliasBean)
			{
				AliasBean alias = (AliasBean) component;
				try
				{
					Method makeAliasMethod = alias.getClass().getDeclaredMethod("makeAlias", FacesContext.class);
					makeAliasMethod.setAccessible(true);
					makeAliasMethod.invoke(alias, context);
				}
				catch (NoSuchMethodException e)
				{
					log.warn(e.getLocalizedMessage(), e);
				}
				catch (IllegalAccessException e)
				{
					log.warn(e.getLocalizedMessage(), e);
				}
				catch (InvocationTargetException e)
				{
					log.warn(e.getLocalizedMessage(), e);
				}
			}

			processAliases(context, component);
		}
	}

	@Override
	public Locale calculateLocale(FacesContext context)
	{
		return original.calculateLocale(context);
	}

	@Override
	public String calculateRenderKitId(FacesContext context)
	{
		return original.calculateRenderKitId(context);
	}

	@Override
	public String getResourceURL(FacesContext context, String url)
	{
		return original.getResourceURL(context, url);
	}

	@Override
	public void renderView(FacesContext context, UIViewRoot uiRoot) throws IOException, FacesException
	{
		original.renderView(context, uiRoot);
	}

	@Override
	public void writeState(FacesContext context) throws IOException
	{
		original.writeState(context);
	}
}


> t:aliasBean + binding + panelGrid causes exception
> --------------------------------------------------
>
>          Key: MYFACES-856
>          URL: http://issues.apache.org/jira/browse/MYFACES-856
>      Project: MyFaces
>         Type: Bug
>     Reporter: Simon Kitching

>
> The following use of t:aliasBean causes an exception:
> ==== including page
> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
> <f:view>
>   <h:panelGrid>
>     <t:aliasBean alias="#{target}" value="#{someBean}">
>       <jsp:include page="included.jsp"/>
>     </t:aliasBean>
>   </h:panelGrid>
> </f:view>
> ==== included page
> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
> <f:subview id="foo">
>     <h:inputText binding="#{target.input}"/>
> </f:subview>
> The thrown exception is:
> javax.faces.el.PropertyNotFoundException: Base is null: target
>         at org.apache.myfaces.el.ValueBindingImpl.resolveToBaseAndProperty(ValueBindingImpl.java:457)
>         at org.apache.myfaces.el.ValueBindingImpl.setValue(ValueBindingImpl.java:245)
>         at org.apache.myfaces.application.ApplicationImpl.createComponent(ApplicationImpl.java:434)
> I believe the problem is that panelGrid renders its children. This means that the components are first created, then the panelGrid iterates over them to render them. However the alias bean hasn't set up the alias at the time the components are being created. And ApplicationImpl.createComponent(binding, context, componentType) is immediately evaluating the binding expression, resulting in the target bean for the binding not being found.

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira


Re: [jira] Closed: (MYFACES-856) t:aliasBean + binding + panelGrid causes exception

Posted by Simon Kitching <sk...@obsidium.com>.
Mike Kienenberger wrote:
> On 11/30/05, Simon Kitching <sk...@obsidium.com> wrote:
>> [1] It would be interesting to know whether this approach would work for
>> Facelets; on first view, what is the timing of binding attribute
>> evaluations with respect to the aliasBean's rendering methods?
> 
> No one who is using facelets will use aliasBean :)
> There are more powerful and predictable alternatives available in facelets.

Thanks. That's one less issue to worry about then.
I do wish I was able to use facelets myself!

Cheers,

Simon

Re: [jira] Closed: (MYFACES-856) t:aliasBean + binding + panelGrid causes exception

Posted by Mike Kienenberger <mk...@gmail.com>.
On 11/30/05, Simon Kitching <sk...@obsidium.com> wrote:
> [1] It would be interesting to know whether this approach would work for
> Facelets; on first view, what is the timing of binding attribute
> evaluations with respect to the aliasBean's rendering methods?

No one who is using facelets will use aliasBean :)
There are more powerful and predictable alternatives available in facelets.

            <ui:include src="/pages/snippet.xhtml">
                <ui:param name="backingBeanAlias1" value="#{myBackingBean1}"/>
                <ui:param name="backingBeanAlias2" value="#{myBackingBean2}"/>
                <ui:param name="backingBeanAlias3" value="#{myBackingBean3}"/>
            </ui:include>

Or it can all just be replaced by a taglib.xml entry so it looks and
works like a real composite component on your page.

    <my:psuedocomponent
        backingBeanAlias1="#{myBackingBean1}"
        backingBeanAlias2="#{myBackingBean2}"
        backingBeanAlias3="#{myBackingBean3}" />

I wouldn't worry about making this work with facelets.

There are definitely going to be issues to consider with using
aliasBean as a creation-time tag instead of a component.   But that
doesn't mean that this isn't a good design choice :)

Re: [jira] Closed: (MYFACES-856) t:aliasBean + binding + panelGrid causes exception

Posted by Simon Kitching <sk...@obsidium.com>.
Mario Ivankovits wrote:
> Hi Simon!
>> Ok, it looks like there is simply no easy answer to making t:aliasBean 
>> work with component binding. I've therefore added information about 
>> this to the AliasBean.java javadoc, and to the wiki. Looks like that 
>> is all that can be done.
>>   
> Just a wild guess (didnt find the time to investigate it into every 
> detail):
> 
> What if we, once the view has been creates, replace alias of the 
> component binding with the real binding, thus we no longer need the 
> alias any more.
> restoreView will automatically have the correct binding then.
> 
> Shouldnt this be possible?

Hmm..interesting thought.

So when the view is being *created*, we use custom code in the Tag to 
ensure that the alias is set up before the child components are created. 
This works around the problem of no callbacks on t:aliasBean occurring 
before the children are created when t:aliasBean is inside a component 
that renders its children [1].

Then in t:aliasBean.encodeEnd, walk the child components:
   * getBinding("binding");
   * replace all occurrences of aliasName with realName
   * setBinding("binding", newBinding)

Yes, it looks like it might be possible. Needs a little tricky parsing, 
though, as:
   <t:aliasBean alias="#{alpha}" value="#{beta.gamma}">
     <t:someComponent binding="#{alpha.delta}"/>
has to become effectively:
     <t:someComponent binding="#{beta.gamma.delta}"/>

[1] It would be interesting to know whether this approach would work for 
Facelets; on first view, what is the timing of binding attribute 
evaluations with respect to the aliasBean's rendering methods?



Cheers,

Simon

Re: [jira] Closed: (MYFACES-856) t:aliasBean + binding + panelGrid causes exception

Posted by Mario Ivankovits <ma...@ops.co.at>.
Hi Simon!
> Ok, it looks like there is simply no easy answer to making t:aliasBean work with component binding. I've therefore added information about this to the AliasBean.java javadoc, and to the wiki. Looks like that is all that can be done.
>   
Just a wild guess (didnt find the time to investigate it into every detail):

What if we, once the view has been creates, replace alias of the 
component binding with the real binding, thus we no longer need the 
alias any more.
restoreView will automatically have the correct binding then.

Shouldnt this be possible?

---
Mario


[jira] Closed: (MYFACES-856) t:aliasBean + binding + panelGrid causes exception

Posted by "Simon Kitching (JIRA)" <de...@myfaces.apache.org>.
     [ http://issues.apache.org/jira/browse/MYFACES-856?page=all ]
     
Simon Kitching closed MYFACES-856:
----------------------------------

    Resolution: Won't Fix

Ok, it looks like there is simply no easy answer to making t:aliasBean work with component binding. I've therefore added information about this to the AliasBean.java javadoc, and to the wiki. Looks like that is all that can be done.

> t:aliasBean + binding + panelGrid causes exception
> --------------------------------------------------
>
>          Key: MYFACES-856
>          URL: http://issues.apache.org/jira/browse/MYFACES-856
>      Project: MyFaces
>         Type: Bug
>     Reporter: Simon Kitching

>
> The following use of t:aliasBean causes an exception:
> ==== including page
> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
> <f:view>
>   <h:panelGrid>
>     <t:aliasBean alias="#{target}" value="#{someBean}">
>       <jsp:include page="included.jsp"/>
>     </t:aliasBean>
>   </h:panelGrid>
> </f:view>
> ==== included page
> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
> <f:subview id="foo">
>     <h:inputText binding="#{target.input}"/>
> </f:subview>
> The thrown exception is:
> javax.faces.el.PropertyNotFoundException: Base is null: target
>         at org.apache.myfaces.el.ValueBindingImpl.resolveToBaseAndProperty(ValueBindingImpl.java:457)
>         at org.apache.myfaces.el.ValueBindingImpl.setValue(ValueBindingImpl.java:245)
>         at org.apache.myfaces.application.ApplicationImpl.createComponent(ApplicationImpl.java:434)
> I believe the problem is that panelGrid renders its children. This means that the components are first created, then the panelGrid iterates over them to render them. However the alias bean hasn't set up the alias at the time the components are being created. And ApplicationImpl.createComponent(binding, context, componentType) is immediately evaluating the binding expression, resulting in the target bean for the binding not being found.

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira


[jira] Commented: (MYFACES-856) t:aliasBean + binding + panelGrid causes exception

Posted by "Mario Ivankovits (JIRA)" <de...@myfaces.apache.org>.
    [ http://issues.apache.org/jira/browse/MYFACES-856?page=comments#action_12358339 ] 

Mario Ivankovits commented on MYFACES-856:
------------------------------------------

You are right, these are the downsides of this solution, but as I said, it isnt a solution its a HACK.

Even if you put your aliasbean directly after f:view, the aliased binding cant be resolved if you dont call "makeAlias".


You might also have a look at MYFACES-334 (reported by me ;-) )

There Mathias Broekelmann pointed out another solution. Mainly to bind against a non aliased request bean which acts as an interface, though, this leads to not well maintainable code I think.

Might it be possible to treat aliases static and thus, once the binding has been resolved, to rewrite the binding information directly in the component?

> t:aliasBean + binding + panelGrid causes exception
> --------------------------------------------------
>
>          Key: MYFACES-856
>          URL: http://issues.apache.org/jira/browse/MYFACES-856
>      Project: MyFaces
>         Type: Bug
>     Reporter: Simon Kitching

>
> The following use of t:aliasBean causes an exception:
> ==== including page
> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
> <f:view>
>   <h:panelGrid>
>     <t:aliasBean alias="#{target}" value="#{someBean}">
>       <jsp:include page="included.jsp"/>
>     </t:aliasBean>
>   </h:panelGrid>
> </f:view>
> ==== included page
> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
> <f:subview id="foo">
>     <h:inputText binding="#{target.input}"/>
> </f:subview>
> The thrown exception is:
> javax.faces.el.PropertyNotFoundException: Base is null: target
>         at org.apache.myfaces.el.ValueBindingImpl.resolveToBaseAndProperty(ValueBindingImpl.java:457)
>         at org.apache.myfaces.el.ValueBindingImpl.setValue(ValueBindingImpl.java:245)
>         at org.apache.myfaces.application.ApplicationImpl.createComponent(ApplicationImpl.java:434)
> I believe the problem is that panelGrid renders its children. This means that the components are first created, then the panelGrid iterates over them to render them. However the alias bean hasn't set up the alias at the time the components are being created. And ApplicationImpl.createComponent(binding, context, componentType) is immediately evaluating the binding expression, resulting in the target bean for the binding not being found.

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira


[jira] Commented: (MYFACES-856) t:aliasBean + binding + panelGrid causes exception

Posted by "Simon Kitching (JIRA)" <de...@myfaces.apache.org>.
    [ http://issues.apache.org/jira/browse/MYFACES-856?page=comments#action_12358216 ] 

Simon Kitching commented on MYFACES-856:
----------------------------------------

UIComponentTag.doStartTag calls encodeBegin() only for components that do *not* render their children. For components like UIPanel that render their children, *no* calls of any sort are made on the component until after all the child components have been created. But that's too late in this case, as the create will fail due to the binding EL being evaluated as the child components are created :-(

I'm looking into whether is is possible to customise the AliasBeanTag class to work around this...

> t:aliasBean + binding + panelGrid causes exception
> --------------------------------------------------
>
>          Key: MYFACES-856
>          URL: http://issues.apache.org/jira/browse/MYFACES-856
>      Project: MyFaces
>         Type: Bug
>     Reporter: Simon Kitching

>
> The following use of t:aliasBean causes an exception:
> ==== including page
> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
> <f:view>
>   <h:panelGrid>
>     <t:aliasBean alias="#{target}" value="#{someBean}">
>       <jsp:include page="included.jsp"/>
>     </t:aliasBean>
>   </h:panelGrid>
> </f:view>
> ==== included page
> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
> <f:subview id="foo">
>     <h:inputText binding="#{target.input}"/>
> </f:subview>
> The thrown exception is:
> javax.faces.el.PropertyNotFoundException: Base is null: target
>         at org.apache.myfaces.el.ValueBindingImpl.resolveToBaseAndProperty(ValueBindingImpl.java:457)
>         at org.apache.myfaces.el.ValueBindingImpl.setValue(ValueBindingImpl.java:245)
>         at org.apache.myfaces.application.ApplicationImpl.createComponent(ApplicationImpl.java:434)
> I believe the problem is that panelGrid renders its children. This means that the components are first created, then the panelGrid iterates over them to render them. However the alias bean hasn't set up the alias at the time the components are being created. And ApplicationImpl.createComponent(binding, context, componentType) is immediately evaluating the binding expression, resulting in the target bean for the binding not being found.

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira