Description

" value="submit"></h:commandLink> is used in a jsp page, which is visited by the user. The user clicks on the link.

Expected behaviour:
The method someBean.someAction() should be called, and the navigation rule which matches the outcome should determine the page to be displayed.

Wrong behaviour:
The method defined in action is not called and the same jsp page is rendered again.

I did some debugging to find the reason of this problem. It seems to me that the server does not recognize that the click on the link is a postback. In line 172 in org.apache.myfaces.renderkit.html.HtmlResponseStateManager, the HTTP Parameter ResponseStateManager.VIEW_STATE_PARAM is checked for existence. If it is there, the request is a callback, and if it is not there, the request is not treated as postback. This parameter is not encoded in the link rendered by h:commandLink, thus the request is not treated as a postback, and the page is just rendered again.

If javaScript rendering is allowed, this works fine because the HTTP parameter ResponseStateManager.VIEW_STATE_PARAM is rendered as a hidden input field, and the javascript code does a form submit.

It seems to me that the problem could be solved by adding the parameter ResponseStateManager.VIEW_STATE_PARAM to the generated link (but I did not check it).

Activity

I created a fix for the problem by appending the correct parameter to the link. In HtmlLinkRendererBase Rev 544646, I added the following after line 401 (sorry for not being able to produce a diff, I'm still trying to understand the myfaces build process):

I'm rather sure this is just a hack and not a real solution, as I am not familiar with the myfaces internals, but maybe it can serve as a start.
After applying the patch, Myfaces recognized the request as a postback but the problem remained. After some more debugging it turned out that the problem now was that the commandLink was wrapped in a form. On decoding the form (UIForm.processDecodes()), the following code line is encountered before the form decodes its children:

if (!isSubmitted()) return;

As the form is not submitted (because a link is not a form submit), the children of the form are not decoded. I do not think that this is the correct behaviour, as this prevents all components that happen to be wrapped in the form tag from being decoded, even if they have nothing else to do with the form. After commenting out the above line of code, the problem was solved. NB: the same line of code exists in UIForm.processValidators() and UIForm.processUpdates(), I'm not sure whether it should be there.

Thomas Fox
added a comment - 31/Jul/07 18:01 I created a fix for the problem by appending the correct parameter to the link. In HtmlLinkRendererBase Rev 544646, I added the following after line 401 (sorry for not being able to produce a diff, I'm still trying to understand the myfaces build process):
hrefBuf.append('&');
hrefBuf.append(ResponseStateManager.VIEW_STATE_PARAM);
hrefBuf.append('=');
hrefBuf.append(
StateUtils.construct(
new Object[]
{null, null, facesContext.getViewRoot().getId()}
,
facesContext.getExternalContext()));
I'm rather sure this is just a hack and not a real solution, as I am not familiar with the myfaces internals, but maybe it can serve as a start.
After applying the patch, Myfaces recognized the request as a postback but the problem remained. After some more debugging it turned out that the problem now was that the commandLink was wrapped in a form. On decoding the form (UIForm.processDecodes()), the following code line is encountered before the form decodes its children:
if (!isSubmitted()) return;
As the form is not submitted (because a link is not a form submit), the children of the form are not decoded. I do not think that this is the correct behaviour, as this prevents all components that happen to be wrapped in the form tag from being decoded, even if they have nothing else to do with the form. After commenting out the above line of code, the problem was solved. NB: the same line of code exists in UIForm.processValidators() and UIForm.processUpdates(), I'm not sure whether it should be there.

Martin Marinschek
added a comment - 28/Sep/07 15:54 Hi Thomas,
but what if the state is longer than the 2kBs IE allows? This is essentially why the commandLink needs JavaScript - the state can be larger than this.
regards,
Martin

First, I used server state saving in testing the proposed patch, so I did not run into the 2k limit.

Second, I was still a jsf greenhorn when proposing this patch. At that time, I was not aware how much limitations a non-javascript implementations would have. So maybe it is better to leave the standard commandLink component as it is and fail cleanly without javascript. The users willing to accepts the limitations of not using javascript could then use custom components.

The remaining problem with this approach is that the form does not allow its children to decode their state if the form is not submitted. I can see the reason behind this; if one has more than one form on a page, the components on the non-submitted form should keep their state. However, this kills any non-javascript approaches. Also, a submit for only one component in a form might be interesting for partial page rendering.

In my opinion, the cleanest approach would be if the form lets each component decide whether it should render itself or not. E.g. a text input field would check whether its associated html parameter is null or not; if it is null it would not decode itself. However, other components like BooleanSelectCheckbox would need to ask the form whether ist is submitted or not: a null value of the associated parameter could mean a) that the form is not submitted or b) that the form is submitted but the checkbox is not checked.

The drawback in changing this behaviour is that it breaks the behaviour of custom components in a multi-form page: These components are not aware of that they should decide whether to decode themselves or not.

Do you think the "let the components decide whether to decode themselves" approach has any chance of being incorporated into the standard faces distributions ? or are the compatibility problems too big ?

Thomas Fox
added a comment - 11/Oct/07 11:16 Sorry for taking so long to respond; a lot of work kept me.
First, I used server state saving in testing the proposed patch, so I did not run into the 2k limit.
Second, I was still a jsf greenhorn when proposing this patch. At that time, I was not aware how much limitations a non-javascript implementations would have. So maybe it is better to leave the standard commandLink component as it is and fail cleanly without javascript. The users willing to accepts the limitations of not using javascript could then use custom components.
The remaining problem with this approach is that the form does not allow its children to decode their state if the form is not submitted. I can see the reason behind this; if one has more than one form on a page, the components on the non-submitted form should keep their state. However, this kills any non-javascript approaches. Also, a submit for only one component in a form might be interesting for partial page rendering.
In my opinion, the cleanest approach would be if the form lets each component decide whether it should render itself or not. E.g. a text input field would check whether its associated html parameter is null or not; if it is null it would not decode itself. However, other components like BooleanSelectCheckbox would need to ask the form whether ist is submitted or not: a null value of the associated parameter could mean a) that the form is not submitted or b) that the form is submitted but the checkbox is not checked.
The drawback in changing this behaviour is that it breaks the behaviour of custom components in a multi-form page: These components are not aware of that they should decide whether to decode themselves or not.
Do you think the "let the components decide whether to decode themselves" approach has any chance of being incorporated into the standard faces distributions ? or are the compatibility problems too big ?

Use the solution cause the state is encoded twice (the values for state are not the same).

2. The question in deep is: how to submit a form through a link without javascript? I have tried a lot in several ways for many days to found a way to do this and I have not found any solution that meet this conditions.

The previous solution raises an exception on myfaces 1.2.

server state saving

java.lang.NumberFormatException: null
at java.lang.Integer.parseInt(Integer.java:415)
at java.lang.Integer.valueOf(Integer.java:526)
at org.apache.myfaces.application.jsp.JspStateManagerImpl.getServerStateId(JspStateManagerImpl.java:218)
at org.apache.myfaces.application.jsp.JspStateManagerImpl.restoreView(JspStateManagerImpl.java:295)
at org.apache.myfaces.application.jsp.JspViewHandlerImpl.restoreView(JspViewHandlerImpl.java:506)
at org.apache.myfaces.lifecycle.RestoreViewExecutor.execute(RestoreViewExecutor.java:85)

client state saving

java.lang.NullPointerException
at org.apache.myfaces.application.TreeStructureManager.restoreTreeStructure(TreeStructureManager.java:103)
at org.apache.myfaces.application.jsp.JspStateManagerImpl.restoreView(JspStateManagerImpl.java:305)
at org.apache.myfaces.application.jsp.JspViewHandlerImpl.restoreView(JspViewHandlerImpl.java:506)
at org.apache.myfaces.lifecycle.RestoreViewExecutor.execute(RestoreViewExecutor.java:85)

Let's suppose that we can encode the link and solve the previous exception. Suppose a typical form with a input field (h:inputText). When the link is clicked, the value of the input field is not encoded on the link, so the result is that this value is never send to the server. So, this solution (if we can make this work) only works for forms without input controls.

In this scenario, the best is do something like this on the renderer (maybe better on encodeBegin)

Leonardo Uribe
added a comment - 01/Apr/08 03:38 Checking the javascript code of myfaces, I have found some thoughts about this issue:
1. I have run the solution provided, and renders something like this:
<form id="form" name="form" method="post" action="/myfaces-examples12/helloWorld.jsf" enctype="application/x-www-form-urlencoded">
.....
<a href="/myfaces-examples12/helloWorld.jsf?form:_idcl=form:link1&javax.faces.ViewState=8EgC7hvJoXWgMHaUZxk5rx66APlnNueyP32ajDxbvc/i5akMf2jX5RwThYAU4FDpr0IJMZQjJ1Izg7ssnvZWuedBpxy9wCxB" id="form:link1">press me</a>
....
<input type="hidden" name="form_SUBMIT" value="1" /><input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="8EgC7hvJoXWgMHaUZxk5rx66APlnNueyP32ajDxbvc/i5akMf2jX5eYYlouo1labftL/yNL5lR+Sl3m2fYcxyRRj3PDXGGRM" />
</form>
Use the solution cause the state is encoded twice (the values for state are not the same).
2. The question in deep is: how to submit a form through a link without javascript? I have tried a lot in several ways for many days to found a way to do this and I have not found any solution that meet this conditions.
The previous solution raises an exception on myfaces 1.2.
server state saving
java.lang.NumberFormatException: null
at java.lang.Integer.parseInt(Integer.java:415)
at java.lang.Integer.valueOf(Integer.java:526)
at org.apache.myfaces.application.jsp.JspStateManagerImpl.getServerStateId(JspStateManagerImpl.java:218)
at org.apache.myfaces.application.jsp.JspStateManagerImpl.restoreView(JspStateManagerImpl.java:295)
at org.apache.myfaces.application.jsp.JspViewHandlerImpl.restoreView(JspViewHandlerImpl.java:506)
at org.apache.myfaces.lifecycle.RestoreViewExecutor.execute(RestoreViewExecutor.java:85)
client state saving
java.lang.NullPointerException
at org.apache.myfaces.application.TreeStructureManager.restoreTreeStructure(TreeStructureManager.java:103)
at org.apache.myfaces.application.jsp.JspStateManagerImpl.restoreView(JspStateManagerImpl.java:305)
at org.apache.myfaces.application.jsp.JspViewHandlerImpl.restoreView(JspViewHandlerImpl.java:506)
at org.apache.myfaces.lifecycle.RestoreViewExecutor.execute(RestoreViewExecutor.java:85)
Let's suppose that we can encode the link and solve the previous exception. Suppose a typical form with a input field (h:inputText). When the link is clicked, the value of the input field is not encoded on the link, so the result is that this value is never send to the server. So, this solution (if we can make this work) only works for forms without input controls.
In this scenario, the best is do something like this on the renderer (maybe better on encodeBegin)
protected void renderCommandLinkStart(FacesContext facesContext, UIComponent component,
String clientId,
Object value,
String style,
String styleClass)
throws IOException
{
{
String[] anchorAttrsToRender;
if (JavascriptUtils.isJavascriptAllowed(facesContext.getExternalContext()))
{
renderJavaScriptAnchorStart(facesContext, writer, component, clientId);
anchorAttrsToRender = HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_ONCLICK_WITHOUT_STYLE;
}
else
{
throw new javax.faces.FacesException("h:commandLink not supported with javascript enabled");
//renderNonJavaScriptAnchorStart(facesContext, writer, component, clientId);
//anchorAttrsToRender = HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_STYLE;
}
This only make fails apps with h:commandLink and no javascript enabled.

Hmm..what exactly is the required behaviour of an h:commandLink? I had always assumed (and myfaces has always implemented) it works just like h:commandButton. Should check this.

But assuming that commandLink and commandButton should have the same behaviour (just with different visual representation) then commandLink really must submit the enclosing form. As Leonardo points out, without that any data that the user enters into a form will be lost.

And I'm pretty sure there is no way to submit a form from a link without script. Http was designed with the idea that a link and a form are two quite different things; links do not vary depending on the state of other input fields on the page.

However I had thought that a solution to this no-javascript issue had already been implemented: for h:commandLink to render a button with a css style that makes it look like a link. This certainly was being discussed - was it not committed? I'm not 100% sure how this works for disabled users (eg screen-readers for the blind) but initially it seems ok; a submit button should be understandable. Probably better than a link that triggers javascript to submit some not-obviously-related form.

(2)

To repeat an earlier comment: the view state can be of any size but a link has a fixed limit on the size of its query parameters. So any kind of solution implemented with links should just refuse to work with client-side state (throw an exception). Otherwise, a page will work with N components, then suddenly fail with N+1 components. That's really ugly.

Supporting server-side state is ok; the link just needs query params that contain the "key" of the server-side state data.

(3)

I can imagine a new component, called "t:goLink" or some similar name. It could do a postback and run action listeners. However it could never implement the full expected behaviour of h:commandLink (see comment 1 above). Because of the loss of user input, it is really useful only for cases where the action always navigates to some other page (so should probably be hard-wired with immediate=true).

And it would only work reliably with server-side state (see comment 2 above).

(4)
Yes, if h:commandLink is used but the browser is known to not support javascript then some kind of warning or error would probably be a good idea. However I'm not sure that this can be reliably detected on the server. Can JavascriptUtils.isJavascriptAllowed really work? Maybe instead h:form could check whether any component has output javascript into the page, and if so then render a <noscript> tag containing a warning message? Certainly IE and Firefox will render a noscript block iff javascript is disabled for that page..

Simon Kitching
added a comment - 01/Apr/08 09:26 (1)
Hmm..what exactly is the required behaviour of an h:commandLink? I had always assumed (and myfaces has always implemented) it works just like h:commandButton. Should check this.
But assuming that commandLink and commandButton should have the same behaviour (just with different visual representation) then commandLink really must submit the enclosing form. As Leonardo points out, without that any data that the user enters into a form will be lost.
And I'm pretty sure there is no way to submit a form from a link without script. Http was designed with the idea that a link and a form are two quite different things; links do not vary depending on the state of other input fields on the page.
However I had thought that a solution to this no-javascript issue had already been implemented: for h:commandLink to render a button with a css style that makes it look like a link. This certainly was being discussed - was it not committed? I'm not 100% sure how this works for disabled users (eg screen-readers for the blind) but initially it seems ok; a submit button should be understandable. Probably better than a link that triggers javascript to submit some not-obviously-related form.
(2)
To repeat an earlier comment: the view state can be of any size but a link has a fixed limit on the size of its query parameters. So any kind of solution implemented with links should just refuse to work with client-side state (throw an exception). Otherwise, a page will work with N components, then suddenly fail with N+1 components. That's really ugly.
Supporting server-side state is ok; the link just needs query params that contain the "key" of the server-side state data.
(3)
I can imagine a new component, called "t:goLink" or some similar name. It could do a postback and run action listeners. However it could never implement the full expected behaviour of h:commandLink (see comment 1 above). Because of the loss of user input, it is really useful only for cases where the action always navigates to some other page (so should probably be hard-wired with immediate=true).
And it would only work reliably with server-side state (see comment 2 above).
(4)
Yes, if h:commandLink is used but the browser is known to not support javascript then some kind of warning or error would probably be a good idea. However I'm not sure that this can be reliably detected on the server. Can JavascriptUtils.isJavascriptAllowed really work? Maybe instead h:form could check whether any component has output javascript into the page, and if so then render a <noscript> tag containing a warning message? Certainly IE and Firefox will render a noscript block iff javascript is disabled for that page..

As written in my comment from 11/Oct/07 03:16 AM, the patch is not desirable in its original form. In fact, the JSF specs say that a commandLink MUST submit the form which is not possible without Javascript as far as I know.
So any solution in the "original" direction would involve a custom component, probably with reduced functionality even with javascript disabled (no form submit). However, as I wrote above, the problem with this approach is if the custom component is inside a form for layout reasons, then the decode method would not be called in the current implementation.

The idea of rendering a button so that it looks like a link would be a good solution to this problem. I'm not sure it is entirely feasible, though. I have just talked to a colleague who tried to do this, and he says that e.g. underlining a text in a button in firefox is a problem even in the current firefox versions. I do not know whether this addition is submitted in myfaces or not.

Thomas Fox
added a comment - 01/Apr/08 09:55 As written in my comment from 11/Oct/07 03:16 AM, the patch is not desirable in its original form. In fact, the JSF specs say that a commandLink MUST submit the form which is not possible without Javascript as far as I know.
So any solution in the "original" direction would involve a custom component, probably with reduced functionality even with javascript disabled (no form submit). However, as I wrote above, the problem with this approach is if the custom component is inside a form for layout reasons, then the decode method would not be called in the current implementation.
The idea of rendering a button so that it looks like a link would be a good solution to this problem. I'm not sure it is entirely feasible, though. I have just talked to a colleague who tried to do this, and he says that e.g. underlining a text in a button in firefox is a problem even in the current firefox versions. I do not know whether this addition is submitted in myfaces or not.

Leonardo Uribe
added a comment - 01/Apr/08 11:21 I have done this simple html test
<html>
<head>
<style type=text/css>
.boton
{
background-color: transparent;
border-width: 0px;
text-decoration: underline;
color:blue;
}
</style>
</head>
<body>
<input type="submit" class="boton" value="Press Me!"></input>
</body>
</html>
Maybe we can do something like this (this also works):
<input type="submit"
style="background-color: transparent;border-width: 0px;text-decoration: underline;color:blue"
value="Press Me!"></input>
I have tested on IE 6 and firefox 2.0.0.13.
The problems with this is that in fact is not an link tag, so css should be set manually. But the better solution at this time.

Leonardo, you are right, underlining works also with Firefox. Sorry for the wrong information.
The strange thing is that I checked my colleague's application and there underlines do not show, although I used exactly the same css class. Must be due to some surrounding element supressing the underline, but I was not able to find out what it is.

So this issue should be resolved with resolution wont' fix IMHO. I'd offer to do it if you agree.

Thomas Fox
added a comment - 01/Apr/08 11:50 Leonardo, you are right, underlining works also with Firefox. Sorry for the wrong information.
The strange thing is that I checked my colleague's application and there underlines do not show, although I used exactly the same css class. Must be due to some surrounding element supressing the underline, but I was not able to find out what it is.
So this issue should be resolved with resolution wont' fix IMHO. I'd offer to do it if you agree.

I think this issue should remain open, but changed from bug to enhancement. Using "real" links where javascript is supported seems nicer. However when it is disabled (ie ALLOW_JAVASCRIPT=false) it would be nice for h:commandLink to automatically switch to rendering a button rather than just be broken.

However the implementation does require a little more thought; defining the style directly on the button is most convenient but makes it hard for users to override the style (I think direct styles have higher priority than almost any other css rule?). Adding a style-class is better, but myfaces would then need to ensure that the rule was defined; as css rules in HTML need to be defined in the <head> section that becomes a little tricky. Ecch.

Maybe support a config param "org.apache.myfaces.COMMAND_LINK_CLASS"? When set, output that as the "class" for the button written by the commandLink renderer (and the user is responsible for ensuring that a style exists for that class). When the option is not set, hard-wire the style property with the default styling.

Simon Kitching
added a comment - 01/Apr/08 12:16 I think this issue should remain open, but changed from bug to enhancement. Using "real" links where javascript is supported seems nicer. However when it is disabled (ie ALLOW_JAVASCRIPT=false) it would be nice for h:commandLink to automatically switch to rendering a button rather than just be broken.
However the implementation does require a little more thought; defining the style directly on the button is most convenient but makes it hard for users to override the style (I think direct styles have higher priority than almost any other css rule?). Adding a style-class is better, but myfaces would then need to ensure that the rule was defined; as css rules in HTML need to be defined in the <head> section that becomes a little tricky. Ecch.
Maybe support a config param "org.apache.myfaces.COMMAND_LINK_CLASS"? When set, output that as the "class" for the button written by the commandLink renderer (and the user is responsible for ensuring that a style exists for that class). When the option is not set, hard-wire the style property with the default styling.

I think there is no need for the option to be present if the implementation is broken. User doc is not available on the issue (except here and some deep mailing list posts).

I suggest bringing it back as a bug (because it IS broken) - but the solution would be to just remove the option until the implementation works again. User confusion is not good.

Simon's solution is good but should come with a warning that the code is using that mode. converting text to buttons has side effects like broken copy&paste on IE 6. (removes all space before the button for some reason and adding doesn't correct the problem).

Updating user doc with a tip on how to do it would be equally good in my perspective since it's so simple.

Jean-François Poirier
added a comment - 07/Apr/08 21:01 I think there is no need for the option to be present if the implementation is broken. User doc is not available on the issue (except here and some deep mailing list posts).
I suggest bringing it back as a bug (because it IS broken) - but the solution would be to just remove the option until the implementation works again. User confusion is not good.
Simon's solution is good but should come with a warning that the code is using that mode. converting text to buttons has side effects like broken copy&paste on IE 6. (removes all space before the button for some reason and adding doesn't correct the problem).
Updating user doc with a tip on how to do it would be equally good in my perspective since it's so simple.