You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by so...@apache.org on 2013/05/22 05:29:03 UTC

svn commit: r1485037 - in /myfaces/trinidad/trunk: trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/

Author: sobryan
Date: Wed May 22 03:29:03 2013
New Revision: 1485037

URL: http://svn.apache.org/r1485037
Log:
TRINIDAD-2390: More specific URL encoding

Added:
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ExternalContextURLEncoder.java
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/PortletURLEncoder.java   (with props)
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoder.java
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoderFactory.java
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoderWrapper.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/URLEncoderExternalContext.java
Modified:
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLUtils.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java

Added: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ExternalContextURLEncoder.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ExternalContextURLEncoder.java?rev=1485037&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ExternalContextURLEncoder.java (added)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ExternalContextURLEncoder.java Wed May 22 03:29:03 2013
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidad.util;
+
+import java.util.Collections;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+/**
+ * This class contains a URL encoder which delegates to an ExternalContext
+ */
+class ExternalContextURLEncoder extends URLEncoder
+{
+  public ExternalContextURLEncoder(ExternalContext ec)
+  {
+    assert(ec != null);
+    _context = ec;
+  }
+  
+  @Override
+  public String encodePartialActionURL(String url)
+  {
+    return getExternalContext().encodePartialActionURL(url);
+  }
+
+  @Override
+  public String encodeRedirectURL(String url)
+  {
+    return (encodeRedirectURL(url, null));
+  }
+
+  @Override
+  public String encodeRedirectURL(String url, Map<String, List<String>> params)
+  {
+    return getExternalContext().encodeRedirectURL(url, params);
+  }
+
+  @Override
+  public String encodeInProtocolResourceURL(String url)
+  {
+    return getExternalContext().encodeResourceURL(url);
+  }
+  
+  @Override
+  public String encodeSkinResourceURL(String url)
+  {
+    return url;
+  }
+
+  @Override
+  public String encodeActionURL(String url)
+  {
+    return getExternalContext().encodeActionURL(url);
+  }
+  
+  @Override
+  public String encodeResourceURL(String url)
+  {
+    return getExternalContext().encodeResourceURL(url);
+  }
+  
+  @Override
+  public String encodeBookmarkableURL(String url, Map<String, List<String>> params)
+  {
+    return getExternalContext().encodeBookmarkableURL(url, params);
+  }
+  
+  protected ExternalContext getExternalContext()
+  {
+    return _context;
+  }
+  
+  private ExternalContext _context;
+}

Added: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/PortletURLEncoder.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/PortletURLEncoder.java?rev=1485037&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/PortletURLEncoder.java (added)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/PortletURLEncoder.java Wed May 22 03:29:03 2013
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidad.util;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.ExternalContext;
+
+/**
+ *
+ */
+class PortletURLEncoder extends ExternalContextURLEncoder
+{
+  public PortletURLEncoder(ExternalContext ec)
+  {
+    super(ec);
+  }
+
+  @Override
+  public String encodeInProtocolResourceURL(String url)
+  {
+    String enc = getExternalContext().getResponseCharacterEncoding();
+    return super.encodeInProtocolResourceURL(URLUtils.encodeURL(url, _IN_PROTOCOL_MAP, enc));
+  }
+
+  private static final Map <String, List<String>> _IN_PROTOCOL_MAP = Collections.singletonMap("javax.portlet.faces.InProtocolResourceLink", Collections.singletonList("true"));
+}
+

Propchange: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/PortletURLEncoder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoder.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoder.java?rev=1485037&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoder.java (added)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoder.java Wed May 22 03:29:03 2013
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidad.util;
+
+import java.io.Serializable;
+
+import java.io.UnsupportedEncodingException;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+/**
+ * Encodes URL's based on thier type.  While the ExternalContext does this to
+ * some extent, the types of URL's it encodes are often ill-defined.  This utility
+ * method allows the caller to ensure that URL's are encoded in the proper fashion
+ * depending on which container is active at the time.
+ * <p/>
+ * Out of the box, this class supports Servlet and Portlet encoding, but it may
+ * be extended on a per-request basis to support other types of URL encoding with
+ * the use of the "registerURLEncoder" method.
+ * <p/>
+ * It is also important to note that this does not impact the encoding done by the
+ * ExternalContext.  As such, all current applications should work without
+ * modification if they do not choose to use this API for encoding.
+ */
+public abstract class URLEncoder
+{
+  /**
+   * This function should be the same as the {@link ExternalContext#encodeActionURL(String)}
+   * method.  By default it call the code in the ExternalContext.  The reason its
+   * provided here is that certain URLEncoderUtility instances may wish to override
+   * the default functionality and have the ExternalContext pull its default encoding
+   * from here.
+   */
+  public abstract String encodeActionURL(String url);
+  
+  /**
+   * This encodes a URL so that it can be used to make a PPR request in all containers.
+   * JSF 2.0 has the ability to encode URL's in such a fashion, but this is missing
+   * from JSF 1.2 containers.  This method provides the same functionality for JSF 1.2.
+   * 
+   * @param url the unencoded url
+   * @return the encoded url
+   * 
+   * @throws IllegalArgumentException if the URL cannot be encoded
+   */
+  public abstract String encodePartialActionURL(String url);
+  
+  /**
+   * Encodes a url to be explicitly used for a redirect.  In some containers, this is
+   * encoded as-is.  In other containers this may not be encoded or must contain a
+   * fully qualified url.  By default this method calls {@link #encodeRedirectURL(String,Map<String,List<String>>)}
+   * with an empty parameter map.
+   * 
+   * @param url the unencoded url
+   * @return the encoded url
+   * 
+   * @throws IllegalArgumentException if the URL cannot be encoded
+   */
+  public abstract String encodeRedirectURL(String url);
+
+  /**
+   * Encodes a url to be explicitly used for a redirect.  In some containers, this is
+   * encoded as-is.  In other containers this may not be encoded or must contain a
+   * fully qualified url.  This version of the method allows for a map of parameters
+   * to be included in the URL.  These parameters will generate a query string and add
+   * it to a URL that may or may-not already have its own existing query parameters.
+   * The parameter values should be encoded appropriately for the environment so that
+   * the resulting URL can be used at the target of the redirect.
+   * 
+   * The default implementation throws and UnsupportedOperationException and is procided
+   * for the sole purpose of maintaining binary compatibility. 
+   * 
+   * @param url the unencoded url
+   * @return the encoded url
+   * @throws IllegalArgumentException if the URL cannot be encoded
+   * @throws UnsupportedOperationException if the implementation of the URLEncoder is not implemented.
+   * 
+   * @since 2.1
+   * @see ExternalContext#encodeRedirectURL
+   */
+  public String encodeRedirectURL(String url, Map<String, List<String>> parameters)
+  {
+    throw new UnsupportedOperationException();
+  }
+  
+  /**
+   * Encodes a url as a resource.  Generally speaking this will be equivalent to
+   * {@link ExternalContext#encodeResourceURL(String)}.  The url returned from this
+   * method is NOT guarenteed to be in-protocol (meaning that it MAY not have access
+   * to session information).  The advantage of encoding something in this fashion
+   * is that in certain types of containers, like portals, the URL generated may 
+   * have faster access and will generally work better for the purposes of caching
+   * do to its RESTful state.
+   * 
+   * @param url the unencoded url
+   * @return the encoded url
+   * 
+   * @throws IllegalArgumentException if the URL cannot be encoded
+   */
+  public abstract String encodeResourceURL(String url);
+  
+  /**
+   * Encodes a url to a resource such that it is inProtocol.  This means that the
+   * URL returned will be encoded in such a way that the resource will have access
+   * to the parent application's session.  While these URL's do have access to the
+   * session information, they may not be written in a format that is easily cachable.
+   * 
+   * @param url the unencoded url
+   * @return the encoded url
+   * 
+   * @throws IllegalArgumentException if the URL cannot be encoded
+   */
+  public abstract String encodeInProtocolResourceURL(String url);
+  
+  /**
+   * Encodes a resource URL that is mapped to a skinning resources.  Trinidad has
+   * an extensive skinning system that allows for extraction of certain properties
+   * like images so that they can be used with the componentry.  Generally these
+   * image resources are on the same server and whatnot as the actual skin.  In
+   * a servlet environment, this is always the same server, but in portal-like
+   * environments these resources may be on different servers.  This encodes a
+   * URL that comes from a skin and sends it to the right location.
+   * 
+   * @param url
+   * @return
+   */
+  public abstract String encodeSkinResourceURL(String url);
+  
+  /**
+   * The purpose of this method is to generate a query string from the collection of Parameter 
+   * objects provided by the parameters argument and append that query string to the baseUrl. This 
+   * method must be able to encode the parameters to a baseUrl that may or may not have existing 
+   * query parameters. The parameter values should be encoded appropriately for the environment 
+   * so that the resulting URL can be used as the target of a link (e.g., in an href attribute) in 
+   * a JSF response. It's possible for an ExternalContext implementation to override this method in 
+   * any way that would make the URL bookmarkable in that environment.
+   * <p/>
+   * The default implementation simply throws an UnsupportedOperationException and is provided only
+   * for the purposes of ensuring binary compatibility.
+   * 
+   * @param url the base url
+   * @param params a map of parameters
+   * @return
+   * 
+   * @throws UnsupportedOperationException if the default implementation does not support encoding
+   *         of bookmarkable urls.
+   */
+  public String encodeBookmarkableURL(String url, Map<String, List<String>> params)
+  {
+    throw new UnsupportedOperationException();
+  }
+}

Added: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoderFactory.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoderFactory.java?rev=1485037&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoderFactory.java (added)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoderFactory.java Wed May 22 03:29:03 2013
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidad.util;
+
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+/**
+ * This is a factory for the URLEncoder.  The URL encoder is a convenience class that
+ * handles URL encoding in a container independant fashion.  The Portlet bridge spec
+ * and later JSF specifications have tried to make URL encoding more specific, but
+ * these technologies do not always address the needs of Trinidad URL encoding in
+ * all circumstances.  This method aims to do just that.
+ * <p>
+ * This factory can both create URLEncoders as well as keep track of an encoder on
+ * a per-request basis.  Additionally, custom URLEncoders can be added manually via
+ * the {@link #setURLEncoder} method.
+ */
+public class URLEncoderFactory
+{
+  /**
+   * Returns the current URLEncoderFactory.
+   * 
+   * @return a URLEncoder factory
+   */
+  public static URLEncoderFactory getFactory()
+  {
+    return _ENCODER;
+  }
+  
+  /**
+   * Returns the current URLEncoder if it has been set.  If it has not been set it
+   * creates a new URLEncoder, sets it to the current thread, and returns its value.
+   * 
+   * @return the currently set URL encoder.
+   * @throws IllegalStateException if no controller has been set and FacesContext is
+   *         currently unavailble.
+   */
+  public URLEncoder getURLEncoder()
+  {
+    FacesContext fc = FacesContext.getCurrentInstance();
+    if(null == fc)
+    {
+      return getURLEncoder(null);
+    }
+
+    return getURLEncoder(fc.getExternalContext());
+  }
+  
+  /**
+   * Returns a URLEncoder if one has been set.  If it has not been set, it
+   * creates a new URLEncoder using the provided ExternalContext.
+   * 
+   * @return a URLEncoder for the given request
+   * @throws IllegalStateException if no controller has been set and the ExternalContext object
+   *         is null
+   */
+  public URLEncoder getURLEncoder(ExternalContext ec)
+  {
+    //even though we should wait until we have a faces context and throw an error
+    //if we don't, go ahead and fudge it if the threadlocal is not null.  This just
+    //means that a URLEncoder has already been set for this thread.
+    URLEncoder enc = _local.get();
+    
+    if(null != enc)
+    {
+      return enc;
+    }
+    
+    if(null == ec)
+    {
+      throw new IllegalStateException("An ExternalContext must be a available");
+    }
+    
+    if(ExternalContextUtils.isPortlet(ec))
+    {
+      setURLEncoder(new PortletURLEncoder(ec));
+    }
+    else
+    {
+      setURLEncoder(new ExternalContextURLEncoder(ec));
+    }
+    
+    return _local.get();
+  }
+  
+  public void setURLEncoder(URLEncoder encoder)
+  {
+    _local.set(encoder);
+  }
+  
+  private static final URLEncoderFactory _ENCODER = new URLEncoderFactory();
+  
+  //This threadlocal should get cleaned up when the request is done.  It's handled
+  //by the configurators.
+  private static final ThreadLocal<URLEncoder> _local = ThreadLocalUtils.newRequestThreadLocal();
+  
+}

Added: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoderWrapper.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoderWrapper.java?rev=1485037&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoderWrapper.java (added)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLEncoderWrapper.java Wed May 22 03:29:03 2013
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidad.util;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Wrapper class for the URLEncoder that can be used to wrap and existing encoder and
+ * set it on the factory.
+ */
+public abstract class URLEncoderWrapper
+  extends URLEncoder
+{
+  public abstract URLEncoder getWrapped();
+  
+  /**
+   * Encodes an action url.  By default this method calls into the wrapped encoder and
+   * executes the {@link URLEncoder#encodeActionURL(String)} method.
+   * 
+   * @param url a string containing the url to encode
+   * @return the encoded url
+   */
+  @Override
+  public String encodeActionURL(String url)
+  {
+    return getWrapped().encodeActionURL(url);
+  }
+
+  /**
+   * Encodes a resource url.  By default this method calls into the wrapped encoder and
+   * executes the {@link URLEncoder#encodeResourceURL(String)} method.
+   * 
+   * @param url a string containing the url to encode
+   * @return the encoded url
+   */
+  @Override
+  public String encodeResourceURL(String url)
+  {
+    return getWrapped().encodeResourceURL(url);
+  }
+
+  /**
+   * Encodes a resource url that is meant to be in-protocol.  By default this method calls into the wrapped encoder and
+   * executes the {@link URLEncoder#encodeInProtocolResourceURL(String)} method.
+   * 
+   * @param url a string containing the url to encode
+   * @return the encoded url
+   */
+  @Override
+  public String encodeInProtocolResourceURL(String url)
+  {
+    return getWrapped().encodeInProtocolResourceURL(url);
+  }
+
+  /**
+   * Encodes an action URL that is intended to represent a partial page submit.  By default, this method calls into the wrapped
+   * encoder and executes the {@link URLEncoder#encodePartialActionURL(String)} method.
+   * 
+   * @param url a string containing the url to encode
+   * @return the encoded url
+   */
+  @Override
+  public String encodePartialActionURL(String url)
+  {
+    return getWrapped().encodePartialActionURL(url);
+  }
+
+  /**
+   * Encodes a redirect url.  By default this method calls into the wrapped encoder and
+   * executes the {@link URLEncoder#encodeRedirectURL(String)} method.
+   * 
+   * @param url a string containing the url to encode
+   * @return the encoded url
+   */
+  @Override
+  public String encodeRedirectURL(String url)
+  {
+    return getWrapped().encodeRedirectURL(url);
+  }
+  
+  /**
+   * Encodes a redirect url.  By default this method calls into the wrapped encoder and
+   * executes the {@link URLEncoder#encodeRedirectURL(String, Map<String, List<String>>)} method.
+   * 
+   * @param url a string containing the url to encode
+   * @param params a map containing additional params to add to the querystring
+   * @return the encoded url
+   * @throws UnsupportedOperationException if this method is not implemented or if this type of
+   *         url cannot be encoded.
+   * 
+   * @since 2.1
+   */  
+  @Override
+  public String encodeRedirectURL(String url, Map<String, List<String>> params)
+  {
+    return getWrapped().encodeRedirectURL(url, params);
+  }
+
+  /**
+   * Encodes the URL of a Skin Resource.  By default this method calls into the wrapped encoder and
+   * executes the {@link URLEncoder#encodeSkinResourceURL(String)} method.
+   * 
+   * @param url a string containing additional params to add to the querystring.
+   * @return the encoded url
+   */
+  @Override
+  public String encodeSkinResourceURL(String url)
+  {
+    return getWrapped().encodeSkinResourceURL(url);
+  }
+
+  /**
+   * Encodes a bookmarkable url.  By default this method calls into the wrapped encoder and
+   * executes the {@link URLEncoder#encodeBookmarkableURL(String,Map<String,List<String>>)} method.
+   * 
+   * @param url a string containing additional params to add to the querystring.
+   * @return the encoded url
+   * @throws UnsupportedOperationException if this method is not implemented or if this type of
+   *         url cannot be encoded.
+   *         
+   * @since 2.1
+   */
+  @Override
+  public String encodeBookmarkableURL(String url, Map<String, List<String>> params)
+  {
+    return getWrapped().encodeBookmarkableURL(url, params);
+  }
+}

Modified: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLUtils.java?rev=1485037&r1=1485036&r2=1485037&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLUtils.java (original)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/URLUtils.java Wed May 22 03:29:03 2013
@@ -21,10 +21,23 @@ package org.apache.myfaces.trinidad.util
 import java.io.File;
 import java.io.IOException;
 
+import java.io.UnsupportedEncodingException;
+
 import java.net.JarURLConnection;
 import java.net.URL;
 import java.net.URLConnection;
 
+import java.net.URLDecoder;
+
+import java.net.URLEncoder;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
 public final class URLUtils
 {
   private URLUtils()
@@ -88,4 +101,128 @@ public final class URLUtils
 
     return modified;
   }
+  
+  /**
+   * Encodes a URL (with or without an existing query string) such that the value in the params map are added to them.
+   * A valid character encoding must be provided to ensure the parameters are encoded properly.
+   * 
+   * @param url the base URL
+   * @param params the map of parameters to add, or <code>null</code>
+   * @param characterResponseEncoding the character response encoding
+   * @return the properly encoded url
+   * 
+   * @throws UnsupportedOperationException if the encoding is not supported.
+   */
+  public static String encodeURL(String url, Map<String, List<String>> params, String characterResponseEncoding)
+  {
+    String fragment = null;
+    String queryString = null;
+    Map<String, List<String>> paramMap = null;
+
+    //extract any URL fragment
+    int index = url.indexOf(_URL_FRAGMENT_SEPERATOR);
+    if (index != -1)
+    {
+      fragment = url.substring(index+1);
+      url = url.substring(0,index);
+    }
+
+    //extract the current query string and add the params to the paramMap
+    index = url.indexOf(_URL_QUERY_SEPERATOR);
+    if (index != -1)
+    {
+      queryString = url.substring(index + 1);
+      url = url.substring(0, index);
+      String[] nameValuePairs = queryString.split(_URL_PARAM_SEPERATOR);
+      for (int i = 0; i < nameValuePairs.length; i++)
+      {
+        String[] currentPair = nameValuePairs[i].split(_URL_NAME_VALUE_PAIR_SEPERATOR);
+
+        ArrayList<String> value = new ArrayList<String>(1);
+        try
+        {
+          value.add(currentPair.length > 1
+                    ? URLDecoder.decode(currentPair[1], characterResponseEncoding)
+                    : "");
+        }
+        catch (UnsupportedEncodingException e)
+        {
+          //shouldn't ever get here
+          throw new UnsupportedOperationException("Encoding type=" + characterResponseEncoding
+                                                          + " not supported", e);
+        }
+        if (paramMap == null)
+        {
+          paramMap = new HashMap<String, List<String>>();
+        }
+        paramMap.put(currentPair[0], value);
+      }
+    }
+
+    //add/update with new params on the paramMap
+    if (params != null && params.size() > 0)
+    {
+      for (Map.Entry<String, List<String>> pair : params.entrySet())
+      {
+        if (pair.getKey() != null && pair.getKey().trim().length() != 0)
+        {
+          if (paramMap == null)
+          {
+            paramMap = new HashMap<String, List<String>>();
+          }
+          paramMap.put(pair.getKey(), pair.getValue());
+        }
+      }
+    }
+
+    // start building the new URL
+    StringBuilder newUrl = new StringBuilder(url);
+
+    //now add the updated param list onto the url
+    if (paramMap != null && paramMap.size()>0)
+    {
+      boolean isFirstPair = true;
+      for (Map.Entry<String, List<String>> pair : paramMap.entrySet())
+      {
+        for (String value : pair.getValue())
+        {
+          if (!isFirstPair)
+          {
+            newUrl.append(_URL_PARAM_SEPERATOR);
+          }
+          else
+          {
+            newUrl.append(_URL_QUERY_SEPERATOR);
+            isFirstPair = false;
+          }
+
+          newUrl.append(pair.getKey());
+          newUrl.append(_URL_NAME_VALUE_PAIR_SEPERATOR);
+          try
+          {
+            newUrl.append(URLEncoder.encode(value,characterResponseEncoding));
+          }
+          catch (UnsupportedEncodingException e)
+          {
+            //shouldn't ever get here
+            throw new UnsupportedOperationException("Encoding type=" + characterResponseEncoding
+                                                  + " not supported", e);
+          }
+        }
+      }    
+    }
+    
+    //add the fragment back on (if any)
+    if (fragment != null)
+    {
+      newUrl.append(_URL_FRAGMENT_SEPERATOR + fragment);
+    }
+    
+    return newUrl.toString();
+  }
+  
+  private static final String _URL_PARAM_SEPERATOR="&";
+  private static final String _URL_QUERY_SEPERATOR="?";
+  private static final String _URL_FRAGMENT_SEPERATOR="#";
+  private static final String _URL_NAME_VALUE_PAIR_SEPERATOR="=";
 }
\ No newline at end of file

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java?rev=1485037&r1=1485036&r2=1485037&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java Wed May 22 03:29:03 2013
@@ -360,6 +360,9 @@ public final class GlobalConfiguratorImp
   {
     RequestStateMap state = RequestStateMap.getInstance(ec);
     RequestType type = (RequestType) state.get(_REQUEST_TYPE);
+    
+    //Install the URLEncoder plugin system
+    ec = new URLEncoderExternalContext(ec);
 
     if (type == null)
     {

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/URLEncoderExternalContext.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/URLEncoderExternalContext.java?rev=1485037&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/URLEncoderExternalContext.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/URLEncoderExternalContext.java Wed May 22 03:29:03 2013
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.config;
+
+import java.io.UnsupportedEncodingException;
+
+import java.net.URLDecoder;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.ExternalContext;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.myfaces.trinidad.context.ExternalContextDecorator;
+import org.apache.myfaces.trinidad.util.URLEncoder;
+import org.apache.myfaces.trinidad.util.URLEncoderFactory;
+import org.apache.myfaces.trinidad.util.URLUtils;
+
+public class URLEncoderExternalContext
+  extends ExternalContextDecorator
+{
+  public URLEncoderExternalContext(ExternalContext ec)
+  {
+    _ec = ec;
+  }
+
+  protected ExternalContext getExternalContext()
+  {
+    return _ec;
+  }
+
+  @Override
+  public String encodeBookmarkableURL(String string, Map<String, List<String>> map)
+  {
+    URLEncoder encoder = getURLEncoder();
+    
+    try
+    {
+      return encoder.encodeBookmarkableURL(string, map);
+    }
+    catch(UnsupportedOperationException e)
+    {
+      //This is a valid response if we have an old handler.  In this case, we need to do our own encoding
+      Object response = _ec.getResponse();
+      if (!(response instanceof HttpServletResponse))
+      {
+        throw new UnsupportedOperationException("Only valid for HttpServlet requests");
+      }
+      
+      String url = URLUtils.encodeURL(string, map, _ec.getResponseCharacterEncoding());
+      
+      //For this guy, we simply return the URL I believe.  Other encoding should be done before or after this.
+      return url;
+    }
+  }
+
+  @Override
+  public String encodeNamespace(String string)
+  {
+    // TODO Implement this method
+    return super.encodeNamespace(string);
+  }
+
+  @Override
+  public String encodePartialActionURL(String string)
+  {
+    // TODO Implement this method
+    return super.encodePartialActionURL(string);
+  }
+
+  @Override
+  public String encodeRedirectURL(String string, Map<String, List<String>> map)
+  {
+    URLEncoder encoder = getURLEncoder();
+    
+    try
+    {
+      return encoder.encodeRedirectURL(string, map);
+    }
+    catch(UnsupportedOperationException e)
+    {
+      //This is a valid response if we have an old handler.  In this case, we need to do our own encoding
+      Object response = _ec.getResponse();
+      if (!(response instanceof HttpServletResponse))
+      {
+        throw new UnsupportedOperationException("Only valid for HttpServlet requests");
+      }
+      
+      String url = URLUtils.encodeURL(string, map, _ec.getResponseCharacterEncoding());
+      
+      //this version of the encodeRedirect method MUST be supported.
+      return encoder.encodeRedirectURL(url);
+    }
+  }
+
+  @Override
+  public String encodeResourceURL(String url)
+  {
+    return getURLEncoder().encodeResourceURL(url);
+  }
+
+  @Override
+  public String encodeActionURL(String url)
+  {
+    return getURLEncoder().encodeActionURL(url);
+  }
+  
+  protected URLEncoder getURLEncoder()
+  {
+    return URLEncoderFactory.getFactory().getURLEncoder(_ec);
+  }
+  
+  private ExternalContext _ec;
+}