You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by mf...@apache.org on 2010/05/20 23:26:45 UTC

svn commit: r946795 - in /myfaces/portlet-bridge/testsuite/trunk_2.0.x: portlet-bridge-testsuite-main/src/main/webapp/WEB-INF/ src/main/java/org/apache/myfaces/portlet/faces/testsuite/common/util/ src/main/java/org/apache/myfaces/portlet/faces/testsuit...

Author: mfreedman
Date: Thu May 20 21:26:45 2010
New Revision: 946795

URL: http://svn.apache.org/viewvc?rev=946795&view=rev
Log:
Fixed issues with URL encoding tests:  i.e. use url encoder based on latest rfc AND made sure that all URL compares took into account that the portlet container can return XML & in their results.

Also modified BridgeDestroyTestPortlet to provider better information that it can only be run once per app instance.

Added:
    myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/common/util/HTTPUtils.java
Modified:
    myfaces/portlet-bridge/testsuite/trunk_2.0.x/portlet-bridge-testsuite-main/src/main/webapp/WEB-INF/portlet.xml
    myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/tests/chapter_3/BridgeDestroyTestPortlet.java
    myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/tests/chapter_6/section_6_1_3_1/Tests.java

Modified: myfaces/portlet-bridge/testsuite/trunk_2.0.x/portlet-bridge-testsuite-main/src/main/webapp/WEB-INF/portlet.xml
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/testsuite/trunk_2.0.x/portlet-bridge-testsuite-main/src/main/webapp/WEB-INF/portlet.xml?rev=946795&r1=946794&r2=946795&view=diff
==============================================================================
--- myfaces/portlet-bridge/testsuite/trunk_2.0.x/portlet-bridge-testsuite-main/src/main/webapp/WEB-INF/portlet.xml (original)
+++ myfaces/portlet-bridge/testsuite/trunk_2.0.x/portlet-bridge-testsuite-main/src/main/webapp/WEB-INF/portlet.xml Thu May 20 21:26:45 2010
@@ -3025,51 +3025,41 @@
         </portlet-info>
     </portlet>
     
-    <!-- *************************************************************** -->
-    <!-- *                                                             * -->
-    <!-- * Portlet: SingleRequestTestPortlet      * -->
-    <!-- *                                                             * -->
-    <!-- *************************************************************** -->
     <portlet>
-        <portlet-name>sampleTests-singleRequestTest-portlet</portlet-name>
+        <portlet-name>chapter6_7Tests-restoredViewStateParameterTest-portlet</portlet-name>
         <portlet-class>org.apache.myfaces.portlet.faces.testsuite.common.portlet.GenericFacesTestSuitePortlet</portlet-class>
         
         <init-param>
           <name>javax.portlet.faces.defaultViewId.view</name>
-          <value>/tests/SingleRequestTest.jsp</value>
-        </init-param>
-    
+          <value>/tests/MultiRequestTest.jsp</value>
+        </init-param>   
+        
         <expiration-cache>0</expiration-cache>
 
        <supports>
          <mime-type>text/html</mime-type>
        </supports>
         <portlet-info>
-                <title>SingleRequestTestPortlet</title>
+                <title>chapter6_7Tests-restoredViewStateParameterTest-portlet</title>
         </portlet-info>
     </portlet>
     
-    <!-- *************************************************************** -->
-    <!-- *                                                             * -->
-    <!-- * Portlet: MultiRequestTestPortlet      * -->
-    <!-- *                                                             * -->
-    <!-- *************************************************************** -->
     <portlet>
-        <portlet-name>sampleTests-multiRequestTest-portlet</portlet-name>
+        <portlet-name>chapter6_7Tests-setsIsPostbackAttributeTest-portlet</portlet-name>
         <portlet-class>org.apache.myfaces.portlet.faces.testsuite.common.portlet.GenericFacesTestSuitePortlet</portlet-class>
         
         <init-param>
           <name>javax.portlet.faces.defaultViewId.view</name>
           <value>/tests/MultiRequestTest.jsp</value>
-        </init-param>
-    
+        </init-param>   
+        
         <expiration-cache>0</expiration-cache>
 
        <supports>
          <mime-type>text/html</mime-type>
        </supports>
         <portlet-info>
-                <title>MultiRequestTestPortlet</title>
+                <title>chapter6_7Tests-setsIsPostbackAttributeTest-portlet</title>
         </portlet-info>
     </portlet>
 

Added: myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/common/util/HTTPUtils.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/common/util/HTTPUtils.java?rev=946795&view=auto
==============================================================================
--- myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/common/util/HTTPUtils.java (added)
+++ myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/common/util/HTTPUtils.java Thu May 20 21:26:45 2010
@@ -0,0 +1,786 @@
+/* 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.portlet.faces.testsuite.common.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * This class contains various utility methods for encoding and decoding URIs <code>String</code>
+ * using the set of characters allowed in a URI, as defined in <a
+ * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396 - "Uniform Resource Identifiers (URI):
+ * Generic Syntax"</a>.
+ * 
+ * The method does not suffer from the limitations of the <code>java.net.URLEncoder</code> in that
+ * it provides control over the character encoding used for character to byte conversions and is
+ * capable of encoding characters that are encoded by a sequence of several bytes.
+ * <p>
+ * 
+ * To convert a <code>String</code>, each character is examined in turn:
+ * <ul>
+ * <li>The ASCII characters '<code>a</code>' through '<code>z</code>', '<code>A</code>'
+ * through '<code>Z</code>', and '<code>0</code>' through '<code>9</code>' remain the
+ * same.
+ * <li>Additional 'mark characters', i.e. '<code>-</code>', '<code>_</code>', '<code>.</code>', '<code>!</code>', '<code>~</code>', '<code>*</code>', '<code>'</code>' , '<code>(</code>', '<code>)</code>',
+ * remain the same.
+ * <li>The space character '<code>&nbsp;</code>' is converted into a plus sign '<code>+</code>'.
+ * <li>All other characters are converted into a sequence of bytes using the specified character
+ * encoding, and each of these bytes is then encoded as a string "<code>%<i>xy</i></code>",
+ * where <i>xy</i> is the two-digit hexadecimal representation of the byte value.
+ * </ul>
+ */
+public class HTTPUtils
+{
+  // An array mapping characters to booleans. True means the character can be
+  // included without Hex encoding
+  private static final boolean[] sValidChar = new boolean[128];
+
+  // An array mapping 4 bit values to uppercase Hex digits to speed things up
+  // a bit
+  private static final char[]    sHexLookup = new char[16];
+
+  // Lookup arrays used during base64 encoding/decoding
+  private static char[]          sCharLookup;
+  private static byte[]          sByteLookup;
+
+  // Static initializer block
+  static
+  {
+    // First initialize sValidChar
+    // Allow lower case alphabetic characters
+    for (char c = 'a'; c <= 'z'; c++)
+    {
+      sValidChar[c] = true;
+    }
+    // Allow upper case alphabetic characters
+    for (char c = 'A'; c <= 'Z'; c++)
+    {
+      sValidChar[c] = true;
+    }
+    // Allow numeric characters
+    for (char c = '0'; c <= '9'; c++)
+    {
+      sValidChar[c] = true;
+    }
+    // Allow various 'mark' characters
+    sValidChar['-'] = true;
+    sValidChar['_'] = true;
+    sValidChar['.'] = true;
+    sValidChar['~'] = true;
+    sValidChar['\''] = true;
+ 
+
+    // Also initialize the sHexLookup table
+    for (byte b = 0; b < 16; b++)
+    {
+      sHexLookup[b] = Character.toUpperCase(Character.forDigit(b, 16));
+    }
+
+    // Populate arrays for base64 encoding/decoding
+    sCharLookup = new char[64];
+    sByteLookup = new byte[127];
+
+    byte i = 0;
+    char c;
+    for (c = 'A'; c <= 'Z'; c++)
+    {
+      sCharLookup[i] = c;
+      sByteLookup[c] = i++;
+    }
+    for (c = 'a'; c <= 'z'; c++)
+    {
+      sCharLookup[i] = c;
+      sByteLookup[c] = i++;
+    }
+    for (c = '0'; c <= '9'; c++)
+    {
+      sCharLookup[i] = c;
+      sByteLookup[c] = i++;
+    }
+    sCharLookup[i] = '+';
+    sByteLookup['+'] = i++;
+    sCharLookup[i] = '/';
+    sByteLookup['/'] = i;
+    // Signal EOF with -1 (It's safe because the other bytes are only 6
+    // bits)
+    sByteLookup['='] = -1;
+  }
+
+  /**
+   * Returns the date format used for expiry times in type zero (Netscape) cookies.
+   * 
+   * Also used as the date format for portlet session expiries. Creates a new instance, so thread
+   * safe.
+   * 
+   * @return the date format used for expiry times in type zero (Netscape) cookies
+   */
+  public static DateFormat getCookieDateFormat()
+  {
+    DateFormat format = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.US);
+    // Cookies use GMT Time zone by convention (Rule Britania!)
+    format.setTimeZone(TimeZone.getTimeZone("GMT"));
+    return format;
+  }
+
+  /**
+   * Build a fully qualified URL from the pieces provided.
+   * 
+   * This method does not call any encoding methods, so the path argument has to be properly
+   * encoded.
+   * 
+   * @param scheme
+   *          server protocol.
+   * @param host
+   *          server name.
+   * @param port
+   *          server port.
+   * @param path
+   *          properly encoded relative URL.
+   * @throws IllegalArgumentException
+   *           if scheme, host or port is null.
+   */
+  public static String buildUrlAsString(String scheme, String host, int port, String path)
+  {
+    if (scheme == null || scheme.equals("") || host == null || host.equals("") || port == 0)
+    {
+      throw new IllegalArgumentException("Cannot build a URL using following scheme: " + scheme
+                                         + " host: " + host + " port: " + port + " path: " + path);
+    }
+
+    StringBuilder url = new StringBuilder(200);
+
+    url.append(scheme).append("://").append(host);
+
+    // check for protocol default port number
+    if (scheme.equalsIgnoreCase("http") && port != 80 || scheme.equalsIgnoreCase("https")
+        && port != 443)
+    {
+      url.append(":").append(port);
+    }
+
+    if (path != null)
+    {
+      url.append(path);
+    }
+
+    return url.toString();
+  }
+
+  /**
+   * Encodes a String using the set of characters allowed in a URI.
+   * 
+   * @param value
+   *          <code>String</code> to be translated.
+   * @param encoding
+   *          the Java alias for the character encoding to be used to convert non-ASCII characters
+   *          into bytes (e.g. <code>"UTF8"</code>).
+   * @return the translated <code>String</code>.
+   * @exception UnsupportedEncodingException
+   *              if the given encoding is not a recognised character encoding.
+   */
+  public static String encode(String value, String encoding) throws UnsupportedEncodingException
+  {
+    // Create a buffer that is roughly 1.5 times bigger than the value to
+    // account for possible expansion of the resulting encoded string
+    int len = value.length();
+    StringBuilder out = new StringBuilder(len * 3 / 2);
+
+    for (int charIndex = 0; charIndex < len; charIndex++)
+    {
+      char aChar = value.charAt(charIndex);
+      if (aChar <= 127 && sValidChar[aChar])
+      {
+        out.append(aChar);
+      }
+      else if (aChar == ' ')
+      {
+        out.append('+');
+      }
+      else
+      {
+        byte[] charBytes = String.valueOf(aChar).getBytes(encoding);
+        // For each byte to encode this character, write a '%',
+        // followed by a 2 digit uppercase hex representation of the
+        // byte value
+        for (byte element : charBytes)
+        {
+          out.append('%');
+          // Convert into two Hex digits (and don't worry about the
+          // sign bit, unlike Integer.toHexString()
+          out.append(sHexLookup[(element & 0xF0) >> 4]);
+          out.append(sHexLookup[element & 0x0F]);
+        }
+      }
+    }
+    // The result string should be encodable in pure ASCII
+    return out.toString();
+  }
+
+  /**
+   * Encodes a String using the set of characters allowed in a URI. This method encodes a multibyte
+   * string in UTF8
+   * 
+   * @param value
+   *          <code>String</code> to be translated.
+   * @return the translated <code>String</code>.
+   */
+  public static String encodeUTF(String value)
+  {
+    String encodedValue = null;
+    try
+    {
+      encodedValue = encode(value, "UTF8");
+    }
+    catch (UnsupportedEncodingException e)
+    {
+      // TODO - error handling
+      // should never happen for this method because the
+      // character encoding is constant
+    }
+    return encodedValue;
+  }
+
+  /**
+   * Decodes an encoded String.
+   * 
+   * @param value
+   *          <code>String</code> to be translated.
+   * @param encoding
+   *          the Java alias for the character encoding to be used to convert byte sequences into
+   *          characters(e.g. <code>"UTF8"</code>).
+   * @return the translated <code>String</code>.
+   * @exception UnsupportedEncodingException
+   *              if the given encoding is not a recognised character encoding.
+   */
+  public static String decode(String value, String encoding) throws UnsupportedEncodingException
+  {
+    // optimization!
+    // determine if decoding is actually required
+    if (!needsDecoding(value))
+    {
+      return value;
+    }
+
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    int charIndex = 0;
+    int length = value.length();
+    while (charIndex < length)
+    {
+      char aChar = value.charAt(charIndex);
+      if (aChar == '%')
+      {
+        do
+        {
+          int byteVal = Character.digit(value.charAt(charIndex + 1), 16) << 4
+                        | Character.digit(value.charAt(charIndex + 2), 16);
+          bos.write(byteVal);
+          charIndex += 3;
+        } while (charIndex < length && (aChar = value.charAt(charIndex)) == '%');
+      }
+      else
+      {
+        if (aChar == '+')
+        {
+          bos.write(' ');
+        }
+        else
+        {
+          bos.write(aChar);
+        }
+        charIndex++;
+      }
+    }
+    return bos.toString(encoding);
+  }
+
+  /**
+   * Decodes an encoded UTF8 String.
+   * 
+   * @param value
+   *          <code>String</code> to be translated.
+   * @return the translated <code>String</code>.
+   */
+  public static String decodeUTF(String value)
+  {
+    String decodedValue = null;
+    try
+    {
+      decodedValue = decode(value, "UTF8");
+    }
+    catch (UnsupportedEncodingException e)
+    {
+      // TODO - error handling
+      // should never happen for this method because the
+      // character encoding is constant
+    }
+    return decodedValue;
+  }
+
+  /**
+   * Determines whether the given HTTP status code denotes a client or server error. According to
+   * the HTTP 1.1 spec, the codes have the following categories:
+   * <ul>
+   * <li> 1xx - Informational (new in HTTP/1.1)
+   * <li> 2xx - Success
+   * <li> 3xx - Redirection
+   * <li> 4xx - Client Error
+   * <li> 5xx - Server Error
+   * </ul>
+   * 
+   * @param statusCode
+   *          an HTTP response status code
+   * @return true if the given status code denotes a client or server error
+   */
+  public static boolean isErrorStatusCode(int statusCode)
+  {
+    return statusCode >= 400 && statusCode < 600;
+  }
+
+  /**
+   * Determine if a value needs to be decoded using HTTPUtils.decode()
+   * 
+   * This method assumes that a value is encoded. As such, the existence of '%' (leading character
+   * of an encoded sequence) or '+' (sometimes used to replace <space> characters indicates that
+   * decoding IS required.
+   */
+  public static final boolean needsDecoding(String token)
+  {
+    if (token == null)
+    {
+      return false;
+    }
+
+    return token.indexOf('+') != -1 || token.indexOf('%') != -1;
+  }
+
+  /**
+   * Extracts a cookie value from a list of cookies based on the name.
+   * 
+   * @param cookieName
+   *          Name of the cookie to search for.
+   * @param cookies
+   *          List of cookies of the form "name1=value1; name2=value2; ...; nameN=valueN".
+   * @return The cookie value stored as a String object. null if cookie not found.
+   */
+  public static String getCookie(String cookieName, String cookies)
+  {
+    return getParameterValue(cookieName, cookies);
+  }
+
+  /**
+   * Extracts a parameter value from a list of parameters based on the name.
+   * 
+   * @param paramName
+   *          name of the parameter to search forn
+   * @param params
+   *          list of parameters as a String of the form: "name1=value1; name2=value2; ...;
+   *          nameN=valueN".
+   * @return the parameter value or <code>null</code> if not found
+   */
+  protected static String getParameterValue(String paramName, String params)
+  {
+    int paramNameLen;
+    int paramsLen;
+
+    // Obvious error in arguments?
+    if (paramName == null || (paramNameLen = paramName.length()) == 0)
+    {
+      return null;
+    }
+
+    if (params == null || (paramsLen = params.length()) == 0)
+    {
+      return null;
+    }
+
+    // Try to extract the parameter from the list of parameters
+    // Parameters are defined in a string of the form
+    // name1=value1; name2=value2; ...; nameN=valueN
+    //
+    // The following parse copies the behaviour of
+    // wpugcinb_GetCookieInNewBuffer in /m/wwg/src/wpu.c
+    // and
+    // wpdsesgcv_get_cookie_value in /m/wwg/src/wpdses.c
+    //
+
+    int index = 0;
+
+    for (;;)
+    {
+      // First, eat all the white spaces
+      while (params.charAt(index) == ' ' && index < paramsLen)
+      {
+        index++;
+      }
+
+      if (params.startsWith(paramName, index))
+      {
+        int equalsIndex = params.indexOf('=', index);
+
+        if (equalsIndex != -1)
+        {
+          int i = equalsIndex - 1;
+
+          while (params.charAt(i) == ' ' && i > index)
+          {
+            i--;
+          }
+
+          if (paramNameLen == i - index + 1)
+          {
+            // We've found our parameter
+
+            // Move 1 char past '='
+            int beginIndex = equalsIndex + 1;
+
+            // Check for empty parameter - if so return ""
+            if (beginIndex == paramsLen)
+            {
+              return "";
+            }
+
+            // Find end of token
+            int endIndex = params.indexOf(';', beginIndex);
+            if (endIndex == -1)
+            {
+              endIndex = paramsLen - 1;
+            }
+            else
+            {
+              // Check for empty parameter - if so return ""
+              if (beginIndex == endIndex)
+              {
+                return "";
+              }
+              endIndex--;
+            }
+            // Eat white spaces out in front
+            while (params.charAt(beginIndex) == ' ' && beginIndex < endIndex)
+            {
+              beginIndex++;
+            }
+
+            // Eat white spaces out at end
+            while (params.charAt(endIndex) == ' ' && endIndex > beginIndex)
+            {
+              endIndex--;
+            }
+
+            // Please note that substring takes the endIndex and
+            // subtracts 1
+            // from it. Since our endIndex points to where we want
+            // it to be,
+            // we need to add 1 to it so that everything works out.
+            String paramValue = params.substring(beginIndex, endIndex + 1);
+
+            // check for empty cookie - if so return ""
+            if (0 == paramValue.length())
+            {
+              return "";
+            }
+
+            return paramValue;
+          }
+        }
+      }
+
+      // no match - advance to next parameter
+      index = params.indexOf(';', index);
+
+      // Check for end cases
+      if (index == -1) // no more tokens
+      {
+        return null;
+      }
+      if (index == paramsLen - 1) // nothing following last ';'
+      {
+        return null;
+      }
+
+      index++;
+    }
+  }
+
+  /**
+   * Converts a Java Locale to a String in the format specified for the Accept-Language and xml:lang
+   * attribute, i.e. <a href="http://www.ietf.org/rfc/rfc1766.txt">RFC 1766</a>.
+   */
+  public static String toHTTPLocale(Locale locale)
+  {
+    String language = locale.getLanguage();
+    String country = locale.getCountry();
+    String variant = locale.getVariant();
+    if (country.length() > 0 || variant.length() > 0)
+    {
+      StringBuilder buff = new StringBuilder(20).append(language).append('-')
+                                                          .append(country);
+      if (variant.length() > 0)
+      {
+        buff.append('-').append(variant);
+      }
+      return buff.toString();
+    }
+    else
+    {
+      return language;
+    }
+  }
+
+  /**
+   * Sets a cookie value in a list of cookies based on the name. If the cookie value is null, the
+   * cookie is removed from the list.
+   * 
+   * @param cookieName
+   *          Name of the cookie to set.
+   * @param cookieValue
+   *          Value of the cookie to set.
+   * @param cookies
+   *          List of cookies of the form "name1=value1; name2=value2; ...; nameN=valueN".
+   * @return The new cookie string.
+   */
+  public static String setCookie(String cookieName, String cookieValue, String cookies)
+  {
+    int cookieNameLen;
+    int cookiesLen;
+    String newCookies = cookies;
+
+    // Obvious error in arguments?
+    if (cookieName == null || (cookieNameLen = cookieName.length()) == 0)
+    {
+      return cookies;
+    }
+
+    if (cookies == null || (cookiesLen = cookies.length()) == 0)
+    {
+      if (cookieValue != null)
+      {
+        newCookies = cookieName + "=" + cookieValue;
+      }
+    }
+    else
+    {
+      int index = 0;
+
+      // Set the cookie in the list of cookies
+      // Cookie is a string of the form
+      // name1=value1; name2=value2; ...; nameN=valueN
+      //
+
+      for (;;)
+      {
+        // First, eat all the white spaces
+        while (index < cookiesLen && cookies.charAt(index) == ' ')
+        {
+          index++;
+        }
+
+        if (cookies.startsWith(cookieName, index))
+        {
+          int equalsIndex = cookies.indexOf('=', index);
+
+          if (equalsIndex != -1)
+          {
+            int i = equalsIndex - 1;
+
+            while (cookies.charAt(i) == ' ' && i > index)
+            {
+              i--;
+            }
+
+            if (cookieNameLen == i - index + 1)
+            {
+              // We've found our cookie
+
+              // Move 1 char past '='
+              int beginIndex = equalsIndex + 1;
+
+              // Find end of token
+              int endIndex = cookies.indexOf(';', beginIndex);
+              if (endIndex == -1)
+              {
+                endIndex = cookiesLen - 1;
+              }
+              else
+              {
+                endIndex--;
+              }
+
+              if (cookieValue == null)
+              {
+                newCookies = cookies.substring(0, index) + cookies.substring(endIndex + 1);
+              }
+              else
+              {
+                newCookies = cookies.substring(0, beginIndex) + cookieValue
+                             + cookies.substring(endIndex + 1);
+              }
+
+              return newCookies;
+            }
+          }
+        }
+
+        // no match - advance to next cookie
+        index = cookies.indexOf(';', index);
+
+        // Check for end cases
+        if (index == -1 || // no more tokens
+            index == cookiesLen - 1) // nothing following last
+        // ';'
+        {
+          if (cookieValue != null)
+          {
+            if (newCookies.length() != 0)
+            {
+              newCookies = newCookies + ";";
+            }
+            newCookies = newCookies + cookieName + "=" + cookieValue;
+          }
+          return newCookies;
+        }
+
+        index++;
+      }
+    }
+
+    return newCookies;
+  }
+
+  /**
+   * Base64 encodes the supplied bytes array, using the standard base 64 encoding algorithm.
+   * 
+   * @param bytes
+   *          The byte array to encode
+   * @return The base 64 encoded string representing the byte array
+   */
+  public static String base64Encode(byte[] bytes)
+  {
+
+    /*
+     * The base 64 encoding algorithm works as follows:
+     * 
+     * Divide the input bytes stream into blocks of 3 bytes. Divide the 24 bits of a 3-byte block
+     * into 4 groups of 6 bits. Map each group of 6 bits to 1 printable character, based on the
+     * 6-bit value. If the last 3-byte block has only 1 byte of input data, pad 2 bytes of zero
+     * (\x0000). After encoding it as a normal block, override the last 2 characters with 2 equal
+     * signs (==), so the decoding process knows 2 bytes of zero were padded. If the last 3-byte
+     * block has only 2 bytes of input data, pad 1 byte of zero (\x00). After encoding it as a
+     * normal block, override the last 1 character with 1 equal signs (=), so the decoding process
+     * knows 1 byte of zero was padded.
+     */
+
+    int i = 0;
+    int bytesToWrite = bytes.length;
+
+    StringBuilder buff = new StringBuilder(bytes.length * 4 / 3);
+
+    while (bytesToWrite >= 3)
+    {
+      buff.append(sCharLookup[bytes[i] >>> 2 & 63]);
+
+      buff.append(sCharLookup[((bytes[i] & 3) << 4) + (bytes[i + 1] >>> 4 & 15)]);
+      buff.append(sCharLookup[((bytes[i + 1] & 15) << 2) + (bytes[i + 2] >>> 6 & 3)]);
+      buff.append(sCharLookup[bytes[i + 2] & 63]);
+
+      bytesToWrite -= 3;
+      i = i + 3;
+    }
+    switch (bytesToWrite)
+    {
+      case 2:
+        buff.append(sCharLookup[bytes[i] >>> 2 & 63]);
+        buff.append(sCharLookup[((bytes[i] & 3) << 4) + (bytes[i + 1] >>> 4 & 15)]);
+        buff.append(sCharLookup[((bytes[i + 1] & 15) << 2)]);
+        buff.append('=');
+        break;
+      case 1:
+        buff.append(sCharLookup[bytes[i] >> 2 & 63]);
+        buff.append(sCharLookup[(bytes[i] & 3) << 4]);
+        buff.append('=');
+        buff.append('=');
+    }
+    return buff.toString();
+  }
+
+  /**
+   * Decodes the supplied base 64 encoded string into its original byte array, using the standard
+   * base 64 decoding algorithm.
+   * 
+   * @param string
+   *          The base 64 encoded string to decode
+   * @return The decoded byte array
+   */
+  public static byte[] base64Decode(String string)
+  {
+    // Blocks of encoded data may have newline characters which
+    // must be ignored
+    string = string.replace("\n", "");
+    string = string.replace("\r", "");
+
+    char[] chars = string.toCharArray();
+    int i = 0;
+    int charsToWrite = chars.length;
+    ByteArrayOutputStream buff = new ByteArrayOutputStream(chars.length);
+    byte[] b = new byte[4];
+    while (charsToWrite >= 4)
+    {
+      try
+      {
+        // If we can't get one complete byte, then something has gone
+        // wrong
+        if ((b[0] = sByteLookup[chars[i++]]) == -1 || (b[1] = sByteLookup[chars[i++]]) == -1)
+        {
+          throw new IllegalArgumentException(string);
+        }
+        buff.write(b[0] << 2 | b[1] >>> 4);
+        if ((b[2] = sByteLookup[chars[i++]]) == -1)
+        {
+          charsToWrite -= 4;
+          break;
+        }
+        buff.write(b[1] << 4 | b[2] >>> 2);
+        if ((b[3] = sByteLookup[chars[i++]]) == -1)
+        {
+          charsToWrite -= 4;
+          break;
+        }
+        buff.write(b[2] << 6 | b[3]);
+        charsToWrite -= 4;
+      }
+      // If any of the byte lookups go out of bounds, this can't be a
+      // valid base 64 encoding
+      catch (ArrayIndexOutOfBoundsException aiobe)
+      {
+        throw new IllegalArgumentException(string);
+      }
+    }
+    // If we have any odd characters at the end, then something has gone
+    // wrong
+    if (charsToWrite > 0)
+    {
+      throw new IllegalArgumentException(string);
+    }
+    return buff.toByteArray();
+  }
+}

Modified: myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/tests/chapter_3/BridgeDestroyTestPortlet.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/tests/chapter_3/BridgeDestroyTestPortlet.java?rev=946795&r1=946794&r2=946795&view=diff
==============================================================================
--- myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/tests/chapter_3/BridgeDestroyTestPortlet.java (original)
+++ myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/tests/chapter_3/BridgeDestroyTestPortlet.java Thu May 20 21:26:45 2010
@@ -48,6 +48,7 @@ public class BridgeDestroyTestPortlet ex
   final static String NULLREQUEST_ACTION_TEST = "nullRequestActionTest";
   
   private String mActionResult = null;
+  private boolean mActionResultOutput = false;
   
   public void init(PortletConfig config)
     throws PortletException
@@ -86,6 +87,13 @@ public class BridgeDestroyTestPortlet ex
     else if (getTestName().equals(DESTROY_ACTION_TEST) && mActionResult != null)
     {
       outputActionResult(renderRequest, renderResponse);
+      // This test can't be rerun until the portlet is reloaded
+      // So set a new ActionResult that indicates this
+      if (!mActionResultOutput)
+      {
+        encodeActionDestroyTestAlreadyRun();
+        mActionResultOutput = true;
+      }
     }
     else if (getTestName().equals(NULLREQUEST_RENDER_TEST))
     {
@@ -100,6 +108,16 @@ public class BridgeDestroyTestPortlet ex
       super.doDispatch(renderRequest, renderResponse);
     }
   }
+  
+  private void encodeActionDestroyTestAlreadyRun()
+  {
+    BridgeTCKResultWriter resultWriter = new BridgeTCKResultWriter(DESTROY_ACTION_TEST);
+    
+    resultWriter.setStatus(BridgeTCKResultWriter.FAIL);
+    resultWriter.setDetail("Test result has already been rendered. This can can only be rendered once prior to portlet or app reload.  To rerun this test reload.  The result that has previously been rendered is: " + mActionResult);
+   
+    mActionResult = resultWriter.toString();
+  }
 
 
   private void runActionDestroyTest(ActionRequest request, ActionResponse response) throws PortletException, IOException
@@ -160,7 +178,6 @@ public class BridgeDestroyTestPortlet ex
     response.setContentType("text/html");
     PrintWriter out = response.getWriter();
     out.println(mActionResult);
-    mActionResult = null;
   }
   
   

Modified: myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/tests/chapter_6/section_6_1_3_1/Tests.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/tests/chapter_6/section_6_1_3_1/Tests.java?rev=946795&r1=946794&r2=946795&view=diff
==============================================================================
--- myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/tests/chapter_6/section_6_1_3_1/Tests.java (original)
+++ myfaces/portlet-bridge/testsuite/trunk_2.0.x/src/main/java/org/apache/myfaces/portlet/faces/testsuite/tests/chapter_6/section_6_1_3_1/Tests.java Thu May 20 21:26:45 2010
@@ -22,9 +22,6 @@ package org.apache.myfaces.portlet.faces
 
 import java.io.UnsupportedEncodingException;
 
-import java.net.URLDecoder;
-import java.net.URLEncoder;
-
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.Locale;
@@ -61,12 +58,13 @@ import javax.portlet.faces.BridgeUtil;
 import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
 
+import javax.servlet.http.HttpUtils;
 import javax.servlet.jsp.JspContext;
 
 import org.apache.myfaces.portlet.faces.testsuite.annotation.BridgeTest;
 import org.apache.myfaces.portlet.faces.testsuite.beans.TestRunnerBean;
 import org.apache.myfaces.portlet.faces.testsuite.common.Constants;
-
+import org.apache.myfaces.portlet.faces.testsuite.common.util.HTTPUtils;
 
 
 public class Tests
@@ -233,17 +231,31 @@ public class Tests
 
     final String PORTLET_RENDER_TEST_STRING =
       "portlet:render?param1=value1&param2=value2";
+    final String PORTLET_RENDER_TEST_STRING_XMLENCODED =
+      "portlet:render?param1=value1&amp;param2=value2";
     FacesContext ctx = FacesContext.getCurrentInstance();
     ExternalContext extCtx = ctx.getExternalContext();
     
-    String bridgeEncoded =
-      extCtx.encodeActionURL(PORTLET_RENDER_TEST_STRING);
-
     RenderResponse response = (RenderResponse) extCtx.getResponse();
     PortletURL portletURL = response.createRenderURL();
     portletURL.setParameter("param1", "value1");
     portletURL.setParameter("param2", "value2");
     String portletEncoded = portletURL.toString();
+    
+    // PortletContainers can return "URLs" with strict XML encoding -- as the bridge 
+    // encoding depends on what is past in to it -- make sure we send in a string
+    // with the same encoding as compare string.
+    String bridgeEncoded = null;
+    if (isStrictXhtmlEncoded(portletEncoded))
+    {
+      bridgeEncoded =
+        extCtx.encodeActionURL(PORTLET_RENDER_TEST_STRING_XMLENCODED);
+    }
+    else
+    {
+      bridgeEncoded =
+        extCtx.encodeActionURL(PORTLET_RENDER_TEST_STRING);
+    }
 
     if (bridgeEncoded.equals(portletEncoded))
     {
@@ -272,18 +284,32 @@ public class Tests
 
     final String PORTLET_ACTION_TEST_STRING =
       "portlet:action?param1=value1&param2=value2";
+    final String PORTLET_ACTION_TEST_STRING_XMLENCODED =
+      "portlet:action?param1=value1&amp;param2=value2";
     FacesContext ctx = FacesContext.getCurrentInstance();
     ExternalContext extCtx = ctx.getExternalContext();
 
-    String bridgeEncoded =
-      extCtx.encodeActionURL(PORTLET_ACTION_TEST_STRING);
-
     RenderResponse response = (RenderResponse) extCtx.getResponse();
     PortletURL portletURL = response.createActionURL();
     portletURL.setParameter("param1", "value1");
     portletURL.setParameter("param2", "value2");
     String portletEncoded = portletURL.toString();
     
+    // PortletContainers can return "URLs" with strict XML encoding -- as the bridge 
+    // encoding depends on what is past in to it -- make sure we send in a string
+    // with the same encoding as compare string.
+    String bridgeEncoded = null;
+    if (isStrictXhtmlEncoded(portletEncoded))
+    {
+      bridgeEncoded =
+        extCtx.encodeActionURL(PORTLET_ACTION_TEST_STRING_XMLENCODED);
+    }
+    else
+    {
+      bridgeEncoded =
+        extCtx.encodeActionURL(PORTLET_ACTION_TEST_STRING);
+    }
+    
     if (bridgeEncoded.equals(portletEncoded))
     {
       testRunner.setTestResult(true,
@@ -1123,7 +1149,7 @@ public class Tests
     final String FOREIGNEXTERNALURL_TEST_STRING = "http://www.apache.org";
     FacesContext ctx = FacesContext.getCurrentInstance();
     ExternalContext extCtx = ctx.getExternalContext();
-    if (extCtx.encodeResourceURL(FOREIGNEXTERNALURL_TEST_STRING).equals(((PortletResponse) extCtx.getResponse()).encodeURL(FOREIGNEXTERNALURL_TEST_STRING)))
+    if (extCtx.encodeResourceURL(FOREIGNEXTERNALURL_TEST_STRING).equals(((PortletResponse) extCtx.getResponse()).encodeURL(FOREIGNEXTERNALURL_TEST_STRING).replace("&amp;", "&")))
     {
       testRunner.setTestResult(true,
                                "encodeResourceURL correctly encoded a foreign external URL.");
@@ -1162,7 +1188,7 @@ public class Tests
     try
     {
       verifyString =
-          FOREIGNEXTERNALURL_BACKLINK_VERIFY_STRING + URLEncoder.encode(actionURL,
+          FOREIGNEXTERNALURL_BACKLINK_VERIFY_STRING + HTTPUtils.encode(actionURL,
                                                                         "UTF-8");
     }
     catch (UnsupportedEncodingException e)
@@ -1171,8 +1197,10 @@ public class Tests
                                "Failed because couldn't UTF-8 encode backLink parameter.");
       return Constants.TEST_FAILED;
     }
-
-    if (extCtx.encodeResourceURL(FOREIGNEXTERNALURL_BACKLINK_TEST_STRING).equals(((PortletResponse) extCtx.getResponse()).encodeURL(verifyString)))
+    
+    // According to bridge rules since string passed in isn't xml strict encoded the result won't be as well
+    // So ensure compares match by stripping from the one generated by the portlet container (if it exists)
+    if (extCtx.encodeResourceURL(FOREIGNEXTERNALURL_BACKLINK_TEST_STRING).equals(((PortletResponse) extCtx.getResponse()).encodeURL(verifyString).replace("&amp;", "&")))
     {
       testRunner.setTestResult(true,
                                "encodeResourceURL correctly encoded a foreign external URL with a backLink.");
@@ -1202,7 +1230,7 @@ public class Tests
     ExternalContext extCtx = ctx.getExternalContext();
 
     if (extCtx.encodeResourceURL(RELATIVEURL_TEST_STRING).equals(((PortletResponse) extCtx.getResponse()).encodeURL(extCtx.getRequestContextPath() +
-                                                                                                                    RELATIVEURL_VERIFY_STRING)))
+                                                                                                                    RELATIVEURL_VERIFY_STRING).replace("&amp;", "&")))
     {
       testRunner.setTestResult(true,
                                "encodeResourceURL correctly encoded a resource referenced by a relative path.");
@@ -1242,7 +1270,7 @@ public class Tests
     {
       verifyString =
           extCtx.getRequestContextPath() + RELATIVEURL_BACKLINK_VERIFY_STRING +
-          URLEncoder.encode(actionURL, "UTF-8");
+          HTTPUtils.encode(actionURL, "UTF-8");
     }
     catch (UnsupportedEncodingException e)
     {
@@ -1251,7 +1279,9 @@ public class Tests
       return Constants.TEST_FAILED;
     }
 
-    if (extCtx.encodeResourceURL(RELATIVEURL_BACKLINK_TEST_STRING).equals(((PortletResponse) extCtx.getResponse()).encodeURL(verifyString)))
+    // According to bridge rules since string passed in isn't xml strict encoded the result won't be as well
+    // So ensure compares match by stripping from the one generated by the portlet container (if it exists)
+    if (extCtx.encodeResourceURL(RELATIVEURL_BACKLINK_TEST_STRING).equals(((PortletResponse) extCtx.getResponse()).encodeURL(verifyString).replace("&amp;", "&")))
     {
       testRunner.setTestResult(true,
                                "encodeResourceURL correctly encoded a relative URL with a backLink.");
@@ -1281,7 +1311,7 @@ public class Tests
     ExternalContext extCtx = ctx.getExternalContext();
 
     if (extCtx.encodeResourceURL(URL_TEST_STRING).equals(((PortletResponse) extCtx.getResponse()).encodeURL(extCtx.getRequestContextPath() +
-                                                                                                            URL_TEST_STRING)))
+                                                                                                            URL_TEST_STRING).replace("&amp;", "&")))
     {
       testRunner.setTestResult(true,
                                "encodeResourceURL correctly encoded a resource referenced by a context path relative path.");
@@ -1321,7 +1351,7 @@ public class Tests
     {
       verifyString =
           extCtx.getRequestContextPath() + URL_BACKLINK_VERIFY_STRING +
-          URLEncoder.encode(actionURL, "UTF-8");
+          HTTPUtils.encode(actionURL, "UTF-8");
     }
     catch (UnsupportedEncodingException e)
     {
@@ -1330,7 +1360,9 @@ public class Tests
       return Constants.TEST_FAILED;
     }
 
-    if (extCtx.encodeResourceURL(URL_BACKLINK_TEST_STRING).equals(((PortletResponse) extCtx.getResponse()).encodeURL(verifyString)))
+    // According to bridge rules since string passed in isn't xml strict encoded the result won't be as well
+    // So ensure compares match by stripping from the one generated by the portlet container (if it exists)
+    if (extCtx.encodeResourceURL(URL_BACKLINK_TEST_STRING).equals(((PortletResponse) extCtx.getResponse()).encodeURL(verifyString).replace("&amp;", "&")))
     {
       testRunner.setTestResult(true,
                                "encodeResourceURL correctly encoded an URL with a backLink.");
@@ -1402,7 +1434,7 @@ public class Tests
     try
     {
       verifyString =
-          URL_VIEWLINK_BACKLINK_VERIFY_STRING + URLEncoder.encode(actionURL,
+          URL_VIEWLINK_BACKLINK_VERIFY_STRING + HTTPUtils.encode(actionURL,
                                                                   "UTF-8");
     }
     catch (UnsupportedEncodingException e)