[JAVASERVERFACES_SPEC_PUBLIC-1240] Ajax behavior renderer automatically/incorrectly quotes client behavior context parameters when building script. Created: 10/Nov/13  Updated: 13/Aug/14

Status: Open
Project: javaserverfaces-spec-public
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Minor
Reporter: cduncan Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified


 Description   

In the AjaxBehaviorRenderer.buildAjaxCommand() method, all ClientBehaviorContext.Parameter parameters are automatically quoted using RenderKitUtils.appendProperty(). This makes dynamicly calculated JavaScript values useless. All other calls to RenderKitUtils.appendProperty specify whether to quote parameters or not. Please do the same for ClientBehaviorContext.Parameter parameters.

The following call in AjaxBehaviorRenderer.java around line 250 should pass false.

for (ClientBehaviorContext.Parameter param : params) {
RenderKitUtils.appendProperty(ajaxCommand,
param.getName(),
param.getValue());
}

I cannot use this very nice way of generating scripts in my custom components until this is fixed. I am currently using jsf.ajax.request, which works fine, but, I would rather use the implementation apis since it is much cleaner.



 Comments   
Comment by cduncan [ 10/Nov/13 ]

The most versatile and unobtrusive solution would be to add a "quoted" member variable to the ClientBehaviorContext.Parameter class. The value could be defaulted to true and referenced in the for loop mentioned above. This would not break existing usage since the for loop defaults to quoted currently. This would allow the developer to choose whether they wanted the quotes or not.

Comment by Manfred Riem [ 11/Nov/13 ]

Can you please tell us which version is affected, what application server you are running? And can you send a reproducing example to issues@javaserverfaces.java.net? That will help us tremendously! Thanks!

Comment by cduncan [ 12/Nov/13 ]

Manfred, I am using the Glassfish 4.0 zip version, which looks to be 2.2.0 according to manifest.mf. My proposed solution in the above comment was looking at the 2.2.4 source.

Thanks!
Chris

Comment by Ed Burns [ 13/Nov/13 ]

Thanks for your response about your version stack. It would help a lot if you could send a reproducer to the list, as suggested by Manfred. Whenever an issue comes in that requests a change in behavior, it is very important that we have the ability to verify that this change does not introduce problems.

Comment by Manfred Riem [ 19/Nov/13 ]

As the code currently in place is working correctly and you are asking for an improvement (by passing it the additional boolean) I am going to re-qualify this as an improvement.

Comment by cduncan [ 24/Nov/13 ]

Manfred, this issue is a show stopper bug for me and not an improvement. I cannot use the client behavior mechanism since it quotes the JavaScript parameters. I will attache the modified files, ClientBehaviorContext.java and AjaxBehaviorRenderer.java. Thanks, Chris.

Comment by cduncan [ 24/Nov/13 ]

/*

  • DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    *
  • Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
    *
  • The contents of this file are subject to the terms of either the GNU
  • General Public License Version 2 only ("GPL") or the Common Development
  • and Distribution License("CDDL") (collectively, the "License"). You
  • may not use this file except in compliance with the License. You can
  • obtain a copy of the License at
  • https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
  • or packager/legal/LICENSE.txt. See the License for the specific
  • language governing permissions and limitations under the License.
    *
  • When distributing the software, include this License Header Notice in each
  • file and include the License file at packager/legal/LICENSE.txt.
    *
  • GPL Classpath Exception:
  • Oracle designates this particular file as subject to the "Classpath"
  • exception as provided by Oracle in the GPL Version 2 section of the License
  • file that accompanied this code.
    *
  • Modifications:
  • If applicable, add the following below the License Header, with the fields
  • enclosed by brackets [] replaced by your own identifying information:
  • "Portions Copyright [year] [name of copyright owner]"
    *
  • Contributor(s):
  • If you wish your version of this file to be governed by only the CDDL or
  • only the GPL Version 2, indicate your decision by adding "[Contributor]
  • elects to include this software in this distribution under the [CDDL or GPL
  • Version 2] license." If you don't indicate a single choice of license, a
  • recipient has the option to distribute your version of this file under
  • either the CDDL, the GPL Version 2 or to extend the choice of license to
  • its licensees as provided above. However, if you add GPL Version 2 code
  • and therefore, elected the GPL Version 2 license, then the option applies
  • only if the new code is made subject to such option by the copyright
  • holder.
    */

package javax.faces.component.behavior;

import java.util.Collection;
import java.util.Collections;

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

/**

  • <p class="changed_added_2_0"><strong>ClientBehaviorContext</strong>
  • provides context information that may be useful to
  • {@link javax.faces.component.behavior.ClientBehavior#getScript}
  • implementations.
  • </p>
    *
  • @since 2.0
    */
    public abstract class ClientBehaviorContext {

/**

  • <p class="changed_added_2_0">Creates a ClientBehaviorContext instance.</p>
    *
  • @param context the <code>FacesContext</code> for the current request.
  • @param component the component instance to which the
  • <code>ClientBehavior</code> is attached.
  • @param eventName the name of the behavior event to which the
  • <code>ClientBehavior</code> is attached.
  • @param sourceId the id to use as the ClientBehavior's "source".
  • @param parameters the collection of parameters for submitting
  • ClientBehaviors to include in the request.
  • @return a <code>ClientBehaviorContext</code> instance configured with the
  • provided values.
  • @throws NullPointerException if <code>context</code>,
  • <code>component</code> or <code>eventName</code>
  • is <code>null</code>
    *
  • @since 2.0
    */
    public static ClientBehaviorContext createClientBehaviorContext(FacesContext context,
    UIComponent component,
    String eventName,
    String sourceId,
    Collection<ClientBehaviorContext.Parameter> parameters) { return new ClientBehaviorContextImpl(context, component, eventName, sourceId, parameters); }

/**

  • <p class="changed_added_2_0">Returns the {@link FacesContext}

    for

  • the current request.</p>
    *
  • @since 2.0
    */
    abstract public FacesContext getFacesContext();

/**

  • <p class="changed_added_2_0">Returns the {@link UIComponent}

    that is

  • requesting the {@link ClientBehavior} script.</p>
    *
    * @since 2.0
    */
    abstract public UIComponent getComponent();

    /**
    * <p class="changed_added_2_0">Returns the name of the behavior event
    * for which the ClientBehavior script is being requested. </p>
    *
    * @since 2.0
    */
    abstract public String getEventName();

    /**
    * <p class="changed_added_2_0">Returns an id for use as the
    * {@link ClientBehavior}

    source. ClientBehavior implementations that submit back

  • to the Faces lifecycle are required to identify which component
  • triggered the ClientBehavior-initiated request via the
  • <code>javax.faces.source</code> request parameter. In
  • most cases, th source id can be trivially derived from the element
  • to which the behavior's client-side script is attached - ie. the
  • source id is typically the id of this element. However, in components
  • which produce more complex content, the behavior script may not be able to
  • determine the correct id to use for the javax.faces.source
  • value. The {@link ClientBehaviorContext#getSourceId}

    method allows the component

  • to pass this information into the {@link ClientBehavior#getScript}
  • implementation.</p>
    *
  • @return the id for the behavior's script to use as the "source", or
  • null if the Behavior's script can identify the source from the DOM.
    *
  • @since 2.0
    */
    abstract public String getSourceId();

/**

  • <p class="changed_added_2_0">Returns parameters that "submitting"
  • {@link ClientBehavior}

    implementations should include when posting back data

  • into the Faces lifecycle. If no parameters are specified, this method
  • returns an empty (non-null) collection.</p>
    *
  • @since 2.0
    */
    abstract public Collection<ClientBehaviorContext.Parameter> getParameters();

// Little static member class that provides a default implementation
private static final class ClientBehaviorContextImpl extends ClientBehaviorContext {
private FacesContext context;
private UIComponent component;
private String eventName;
private String sourceId;
private Collection<ClientBehaviorContext.Parameter> parameters;

private ClientBehaviorContextImpl(FacesContext context,
UIComponent component,
String eventName,
String sourceId,
Collection<ClientBehaviorContext.Parameter> parameters) {

if (null == context)

{ throw new NullPointerException(); }

if (null == component) { throw new NullPointerException(); }

if (null == eventName)

{ throw new NullPointerException(); }

this.context = context;
this.component = component;
this.eventName = eventName;
this.sourceId = sourceId;

this.parameters = (parameters == null) ?
Collections.<ClientBehaviorContext.Parameter>emptyList() :
parameters;
}

@Override
public FacesContext getFacesContext() { return context; }

@Override
public UIComponent getComponent() { return component; }

@Override
public String getEventName() { return eventName; }

@Override
public String getSourceId() { return sourceId; }

@Override
public Collection<ClientBehaviorContext.Parameter> getParameters() { return parameters; }
}

/**
* <p class="changed_added_2_0"><strong>Parameter</strong> instances
* represent name/value pairs that "submitting" ClientBehavior implementations
* should include when posting back into the Faces lifecycle. ClientBehavior
* implementations can determine which Parameters to include by calling
* ClientBehaviorContext.getParameters().
* </p>
*
* @since 2.0
*/
public static class Parameter {

private String name;
private Object value;
private boolean quoteValue;

/**
* <p class="changed_added_2_0">Creates a Parameter instance.</p>
* @param name the name of the parameter
* @param value the value of the parameter
* @throws NullPointerException if <code>name</code>
* is null.
*
* @since 2.0
*/
public Parameter(String name, Object value) { this(name, value, true); }

/**
* <p class="changed_added_2_0">Creates a Parameter instance.</p>
* @param name the name of the parameter
* @param value the value of the parameter
* @param quoteValue indicator for value quotes.
* @throws NullPointerException if <code>name</code>
* is null.
*
* @since 2.2.6
*/
public Parameter(String name, Object value, boolean quoteValue) {

if (null == name) { throw new NullPointerException(); }

this.name = name;
this.value = value;
this.quoteValue = quoteValue;
}

/**

  • <p class="changed_added_2_0">Returns the Parameter's name.</p>
    *
  • @since 2.0
    */
    public String getName() { return name; }

/**

  • <p class="changed_added_2_0">Returns the Parameter's value.</p>
    *
  • @since 2.0
    */
    public Object getValue() { return value; }

/**

  • <p class="changed_added_2_2_6">Returns the Parameter's quote value indicator.</p>
    *
  • @since 2.2.6
    */
    public boolean getQuoteValue() { return quoteValue; }

    }
    }

Comment by cduncan [ 24/Nov/13 ]

/*

  • DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    *
  • Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
    *
  • The contents of this file are subject to the terms of either the GNU
  • General Public License Version 2 only ("GPL") or the Common Development
  • and Distribution License("CDDL") (collectively, the "License"). You
  • may not use this file except in compliance with the License. You can
  • obtain a copy of the License at
  • https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
  • or packager/legal/LICENSE.txt. See the License for the specific
  • language governing permissions and limitations under the License.
    *
  • When distributing the software, include this License Header Notice in each
  • file and include the License file at packager/legal/LICENSE.txt.
    *
  • GPL Classpath Exception:
  • Oracle designates this particular file as subject to the "Classpath"
  • exception as provided by Oracle in the GPL Version 2 section of the License
  • file that accompanied this code.
    *
  • Modifications:
  • If applicable, add the following below the License Header, with the fields
  • enclosed by brackets [] replaced by your own identifying information:
  • "Portions Copyright [year] [name of copyright owner]"
    *
  • Contributor(s):
  • If you wish your version of this file to be governed by only the CDDL or
  • only the GPL Version 2, indicate your decision by adding "[Contributor]
  • elects to include this software in this distribution under the [CDDL or GPL
  • Version 2] license." If you don't indicate a single choice of license, a
  • recipient has the option to distribute your version of this file under
  • either the CDDL, the GPL Version 2 or to extend the choice of license to
  • its licensees as provided above. However, if you add GPL Version 2 code
  • and therefore, elected the GPL Version 2 license, then the option applies
  • only if the new code is made subject to such option by the copyright
  • holder.
    */

package com.sun.faces.renderkit.html_basic;

import com.sun.faces.util.FacesLogger;

import java.util.Collection;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.FacesException;
import javax.faces.component.ActionSource;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.behavior.AjaxBehavior;
import javax.faces.component.behavior.ClientBehavior;
import javax.faces.component.behavior.ClientBehaviorContext;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.event.PhaseId;
import javax.faces.render.ClientBehaviorRenderer;

import com.sun.faces.renderkit.RenderKitUtils;

/*
*<b>AjaxBehaviorRenderer</b> renders Ajax behavior for a component.

  • It also
    */

public class AjaxBehaviorRenderer extends ClientBehaviorRenderer {

// Log instance for this class
protected static final Logger logger = FacesLogger.RENDERKIT.getLogger();

// ------------------------------------------------------ Rendering Methods

@Override
public String getScript(ClientBehaviorContext behaviorContext,
ClientBehavior behavior) {
if (!(behavior instanceof AjaxBehavior))

{ // TODO: use MessageUtils for this error message? throw new IllegalArgumentException( "Instance of javax.faces.component.behavior.AjaxBehavior required: " + behavior); }

if (((AjaxBehavior)behavior).isDisabled())

{ return null; }
return buildAjaxCommand(behaviorContext, (AjaxBehavior)behavior);
}


@Override
public void decode(FacesContext context,
UIComponent component,
ClientBehavior behavior) {
if (null == context || null == component || null == behavior) { throw new NullPointerException(); }

if (!(behavior instanceof AjaxBehavior)) { // TODO: use MessageUtils for this error message? throw new IllegalArgumentException( "Instance of javax.faces.component.behavior.AjaxBehavior required: " + behavior); }

AjaxBehavior ajaxBehavior = (AjaxBehavior)behavior;

// First things first - if AjaxBehavior is disabled, we are done.
if (ajaxBehavior.isDisabled()) { return; }

component.queueEvent(createEvent(component, ajaxBehavior));

if (logger.isLoggable(Level.FINE)) {
logger.fine("This command resulted in form submission " +
" AjaxBehaviorEvent queued.");
logger.log(Level.FINE,
"End decoding component {0}", component.getId());
}


}

// Creates an AjaxBehaviorEvent for the specified component/behavior
private static AjaxBehaviorEvent createEvent(UIComponent component,
AjaxBehavior ajaxBehavior) { AjaxBehaviorEvent event = new AjaxBehaviorEvent(component, ajaxBehavior); PhaseId phaseId = isImmediate(component, ajaxBehavior) ? PhaseId.APPLY_REQUEST_VALUES : PhaseId.INVOKE_APPLICATION; event.setPhaseId(phaseId); return event; }


// Tests whether we should perform immediate processing. Note
// that we "inherit" immediate from the parent if not specified
// on the behavior.
private static boolean isImmediate(UIComponent component,
AjaxBehavior ajaxBehavior) {

boolean immediate = false;

if (ajaxBehavior.isImmediateSet()) { immediate = ajaxBehavior.isImmediate(); } else if (component instanceof EditableValueHolder) { immediate = ((EditableValueHolder)component).isImmediate(); } else if (component instanceof ActionSource) { immediate = ((ActionSource)component).isImmediate(); }

return immediate;
}

private static String buildAjaxCommand(ClientBehaviorContext behaviorContext,
AjaxBehavior ajaxBehavior) {

// First things first - if AjaxBehavior is disabled, we are done.
if (ajaxBehavior.isDisabled()) { return null; }

UIComponent component = behaviorContext.getComponent();
String eventName = behaviorContext.getEventName();

StringBuilder ajaxCommand = new StringBuilder(256);
Collection<String> execute = ajaxBehavior.getExecute();
Collection<String> render = ajaxBehavior.getRender();
String onevent = ajaxBehavior.getOnevent();
String onerror = ajaxBehavior.getOnerror();
String sourceId = behaviorContext.getSourceId();
String delay = ajaxBehavior.getDelay();
Boolean resetValues = null;
if (ajaxBehavior.isResetValuesSet())

{ resetValues = ajaxBehavior.isResetValues(); }

Collection<ClientBehaviorContext.Parameter> params = behaviorContext.getParameters();

// Needed workaround for SelectManyCheckbox - if execute doesn't have sourceId,
// we need to add it - otherwise, we use the default, which is sourceId:child, which
// won't work.
ClientBehaviorContext.Parameter foundparam = null;
for (ClientBehaviorContext.Parameter param : params) {
if (param.getName().equals("incExec") && (Boolean)param.getValue())

{ foundparam = param; }

}
if (foundparam != null && !execute.contains(sourceId))

{ execute = new LinkedList<String>(execute); execute.add(component.getClientId()); }

if (foundparam != null) {
try

{ // And since this is a hack, we now try to remove the param params.remove(foundparam); }

catch (UnsupportedOperationException uoe) {
if (logger.isLoggable(Level.FINEST))

{ logger.log(Level.FINEST, "Unsupported operation", uoe); }

}
}

ajaxCommand.append("mojarra.ab(");

if (sourceId == null)

{ ajaxCommand.append("this"); }

else

{ ajaxCommand.append("'"); ajaxCommand.append(sourceId); ajaxCommand.append("'"); }

ajaxCommand.append(",event,'");
ajaxCommand.append(eventName);
ajaxCommand.append("',");

appendIds(component, ajaxCommand, execute);
ajaxCommand.append(",");
appendIds(component, ajaxCommand, render);

if ((onevent != null) || (onerror != null) || (delay != null) ||
(resetValues != null) || !params.isEmpty()) {

ajaxCommand.append(",{");

if (onevent != null)

{ RenderKitUtils.appendProperty(ajaxCommand, "onevent", onevent, false); }

if (onerror != null)

{ RenderKitUtils.appendProperty(ajaxCommand, "onerror", onerror, false); }

if (delay != null)

{ RenderKitUtils.appendProperty(ajaxCommand, "delay", delay, true); }

if (resetValues != null)

{ RenderKitUtils.appendProperty(ajaxCommand, "resetValues", resetValues, false); }

if (!params.isEmpty()) {
for (ClientBehaviorContext.Parameter param : params)

{ RenderKitUtils.appendProperty(ajaxCommand, param.getName(), param.getValue(), param.getQuoteValue()); }

}

ajaxCommand.append("}");
}

ajaxCommand.append(")");

return ajaxCommand.toString();
}

// Appends an ids argument to the ajax command
private static void appendIds(UIComponent component,
StringBuilder builder,
Collection<String> ids) {

if ((null == ids) || ids.isEmpty())

{ builder.append('0'); return; }

builder.append("'");

boolean first = true;

for (String id : ids) {
if (id.trim().length() == 0)

{ continue; }

if (!first)

{ builder.append(' '); }

else

{ first = false; }

if (id.equals("@all") || id.equals("@none") ||
id.equals("@form") || id.equals("@this"))

{ builder.append(id); }

else

{ builder.append(getResolvedId(component, id)); }

}

builder.append("'");
}

// Returns the resolved (client id) for a particular id.
private static String getResolvedId(UIComponent component, String id) {

UIComponent resolvedComponent = component.findComponent(id);
if (resolvedComponent == null)

{ // RELEASE_PENDING i18n throw new FacesException( "<f:ajax> contains an unknown id '" + id + "' - cannot locate it in the context of the component "+component.getId()); }

return resolvedComponent.getClientId();
}
}

Comment by Manfred Riem [ 25/Nov/13 ]

Hi Chris,

I understand your predicament, unfortunately because your change is in javax.faces.component.behavior which is an API package it would violate the TCK (note adding any method to an API package violates the TCK and thus the JSF specification). If you can do it without changing anything in a javax.faces.* package then I certainly would like to get this in, provided it passes all of our tests.

Comment by cduncan [ 25/Nov/13 ]

Manfred:

Thank you for your timely response to this issue and for clarifying changes to the javax.faces.* packages. I was wondering about that before posting the previous solution. I believe there is a simple solution keeping the improvement within the com.sun.faces.renderkit.RenderKitUtils.AjaxBehaviorRenderer class. I will code it up tonight and post a solution that will not interfere with the current usage and allows overrides by component developers.

Thank you,
Chris

Comment by cduncan [ 26/Nov/13 ]

Manfred:

This version of the method com.sun.faces.renderkit.html_basic.AjaxBehaviorRenderer.buildAjaxCommand() explicitly sets the quoted value in the RenderKitUtils.appendProperty() call to false. Currently, it is not explicitly set and thus invokes the overloaded version of RenderKitUtils.appendProperty() which sets the value to true. All of the calls to RenderKitUtils.appendProperty() explicitly set the quoted value except this one. I am not sure if this was an oversight. This version needs to be thoroughly tested to make sure it does not cause any unwanted side effects.

There is another solution if this one does not pass all tests. We can overload the com.sun.faces.renderkit.html_basic.AjaxBehaviorRenderer.getScript() method passing in a boolean indicating whether to quote the value. This boolean will be passed to com.sun.faces.renderkit.html_basic.AjaxBehaviorRenderer.buildAjaxCommand() for the last RenderKitUtils.appendProperty() call.

Thank you,
Chris

private static String buildAjaxCommand(ClientBehaviorContext behaviorContext,
AjaxBehavior ajaxBehavior) {

// First things first - if AjaxBehavior is disabled, we are done.
if (ajaxBehavior.isDisabled())

{ return null; }

UIComponent component = behaviorContext.getComponent();
String eventName = behaviorContext.getEventName();

StringBuilder ajaxCommand = new StringBuilder(256);
Collection<String> execute = ajaxBehavior.getExecute();
Collection<String> render = ajaxBehavior.getRender();
String onevent = ajaxBehavior.getOnevent();
String onerror = ajaxBehavior.getOnerror();
String sourceId = behaviorContext.getSourceId();
String delay = ajaxBehavior.getDelay();
Boolean resetValues = null;
if (ajaxBehavior.isResetValuesSet())

{ resetValues = ajaxBehavior.isResetValues(); }

Collection<ClientBehaviorContext.Parameter> params = behaviorContext.getParameters();

// Needed workaround for SelectManyCheckbox - if execute doesn't have sourceId,
// we need to add it - otherwise, we use the default, which is sourceId:child, which
// won't work.
ClientBehaviorContext.Parameter foundparam = null;
for (ClientBehaviorContext.Parameter param : params) {
if (param.getName().equals("incExec") && (Boolean)param.getValue())

{ foundparam = param; }

}
if (foundparam != null && !execute.contains(sourceId))

{ execute = new LinkedList<String>(execute); execute.add(component.getClientId()); }

if (foundparam != null) {
try

{ // And since this is a hack, we now try to remove the param params.remove(foundparam); }

catch (UnsupportedOperationException uoe) {
if (logger.isLoggable(Level.FINEST))

{ logger.log(Level.FINEST, "Unsupported operation", uoe); }

}
}

ajaxCommand.append("mojarra.ab(");

if (sourceId == null)

{ ajaxCommand.append("this"); }

else

{ ajaxCommand.append("'"); ajaxCommand.append(sourceId); ajaxCommand.append("'"); }

ajaxCommand.append(",event,'");
ajaxCommand.append(eventName);
ajaxCommand.append("',");

appendIds(component, ajaxCommand, execute);
ajaxCommand.append(",");
appendIds(component, ajaxCommand, render);

if ((onevent != null) || (onerror != null) || (delay != null) ||
(resetValues != null) || !params.isEmpty()) {

ajaxCommand.append(",{");

if (onevent != null)

{ RenderKitUtils.appendProperty(ajaxCommand, "onevent", onevent, false); }

if (onerror != null)

{ RenderKitUtils.appendProperty(ajaxCommand, "onerror", onerror, false); }

if (delay != null)

{ RenderKitUtils.appendProperty(ajaxCommand, "delay", delay, true); }

if (resetValues != null)

{ RenderKitUtils.appendProperty(ajaxCommand, "resetValues", resetValues, false); }

if (!params.isEmpty()) {
for (ClientBehaviorContext.Parameter param : params)

{ RenderKitUtils.appendProperty(ajaxCommand, param.getName(), param.getValue(), false); }

}

ajaxCommand.append("}");
}

ajaxCommand.append(")");

return ajaxCommand.toString();
}

Comment by cduncan [ 04/Dec/13 ]

It appears the MyFaces 2.2.0 implementation also automatically quotes the JavaScript parameter values when posting back. The following method is from the HtmlAjaxBehaviorRenderer.java class in the myfaces-impl-2.2.0-beta-sources.jar.

private void append(StringBuilder paramBuffer, List<String> parameterList, ClientBehaviorContext.Parameter param)

{ //TODO we may need a proper type handling in this part //lets leave it for now as it is //quotes etc.. should be transferred directly //and the rest is up to the toString properly implemented //ANS: Both name and value should be quoted paramBuffer.setLength(0); paramBuffer.append(QUOTE); paramBuffer.append(param.getName()); paramBuffer.append(QUOTE); paramBuffer.append(COLON); paramBuffer.append(QUOTE); paramBuffer.append(param.getValue().toString()); paramBuffer.append(QUOTE); parameterList.add(paramBuffer.toString()); }

What I take from this is that the JSF specification for the ClientBehaviorContext.Parameter class is not clear in regards to quoting, and as such, the JSF implementations default to automatic quoting.

In my first solution, I added a 'private boolean quoteValue' member variable to the ClientBehaviorContext.Parameter class and defaulted the value to true. This would not cause any side effects since implementations default to true anyway and would give custom component developers the power to decide if they want their JavaScript value params quoted or not.

As Manfred stated, this would require a change to the JSF spec, but from what I have seen, I believe it is the best route. I do realize that this would take some time, so I am writing my own handling of the client behaviors in my custom components. I cannot tightly couple my custom components to a particular implementation.

Please let me know how I can help further.

Thanks,
Chris

Comment by Manfred Riem [ 04/Dec/13 ]

Chris, thank you for doing the extra work you did. As you stated that MyFaces is also doing automatic quoting I think it might be time to move this issue to the spec issue tracker and put it on the radar for the next version of the JSF specification. What do you think?

Comment by cduncan [ 05/Dec/13 ]

Manfred:

I believe this needs a clarification in the JSF specification. A member variable in the ClientBehaviorContext.Parameter would remove all doubt for the JSF implementors on how to handle the value, and possible name parameter. It could be done in a way that would not affect all current implementations, since they default to quoted currently. Please see my original code solution for suggestions for the JSF specification.

Please let me know if I can help further.

Thanks,
Chris

Comment by Manfred Riem [ 05/Dec/13 ]

Chris, I will go ahead and move this issue to the SPEC issue tracker as it is a SPEC issue. Unfortunately that means resolution will have to wait till the next JSF specification. Thank!

Comment by Ed Burns [ 01/Aug/14 ]

Set priority to baseline ahead of JSF 2.3 triage. Priorities will be assigned accurately after this exercise.

Generated at Thu Mar 05 02:50:14 UTC 2015 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.