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 2010/03/17 10:59:27 UTC

svn commit: r924205 - /myfaces/portlet-bridge/core/branches/sobryan_portlet-bridge-3.0.0/impl/src/main/java/org/apache/myfaces/portlet/faces/context/PortletExternalContextImpl.java

Author: sobryan
Date: Wed Mar 17 09:59:26 2010
New Revision: 924205

URL: http://svn.apache.org/viewvc?rev=924205&view=rev
Log:
Updates to the ExternalContextImpl

Modified:
    myfaces/portlet-bridge/core/branches/sobryan_portlet-bridge-3.0.0/impl/src/main/java/org/apache/myfaces/portlet/faces/context/PortletExternalContextImpl.java

Modified: myfaces/portlet-bridge/core/branches/sobryan_portlet-bridge-3.0.0/impl/src/main/java/org/apache/myfaces/portlet/faces/context/PortletExternalContextImpl.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/core/branches/sobryan_portlet-bridge-3.0.0/impl/src/main/java/org/apache/myfaces/portlet/faces/context/PortletExternalContextImpl.java?rev=924205&r1=924204&r2=924205&view=diff
==============================================================================
--- myfaces/portlet-bridge/core/branches/sobryan_portlet-bridge-3.0.0/impl/src/main/java/org/apache/myfaces/portlet/faces/context/PortletExternalContextImpl.java (original)
+++ myfaces/portlet-bridge/core/branches/sobryan_portlet-bridge-3.0.0/impl/src/main/java/org/apache/myfaces/portlet/faces/context/PortletExternalContextImpl.java Wed Mar 17 09:59:26 2010
@@ -23,14 +23,10 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
-
 import java.io.Writer;
-
 import java.net.MalformedURLException;
 import java.net.URL;
-
 import java.security.Principal;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -45,10 +41,7 @@ import javax.faces.FacesException;
 import javax.faces.application.ViewHandler;
 import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
-
-import javax.faces.context.Flash;
 import javax.faces.render.ResponseStateManager;
-
 import javax.portlet.ActionResponse;
 import javax.portlet.BaseURL;
 import javax.portlet.ClientDataRequest;
@@ -61,14 +54,12 @@ import javax.portlet.PortletRequestDispa
 import javax.portlet.PortletResponse;
 import javax.portlet.PortletSession;
 import javax.portlet.PortletURL;
-import javax.portlet.RenderResponse;
 import javax.portlet.ResourceURL;
 import javax.portlet.StateAwareResponse;
 import javax.portlet.WindowState;
 import javax.portlet.faces.Bridge;
 import javax.portlet.faces.BridgeDefaultViewNotSpecifiedException;
 import javax.portlet.faces.BridgeInvalidViewPathException;
-
 import javax.servlet.http.Cookie;
 
 import org.apache.myfaces.portlet.faces.bridge.BridgeImpl;
@@ -338,11 +329,12 @@ public class PortletExternalContextImpl
   public String encodeActionURL(String url)
   {
     String viewId = null, path = null;
-    boolean nonFacesAction = false;
     QueryString queryStr = null;
     int queryStart = -1;
     boolean isPortletURL = false;
-    Bridge.PortletPhase urlType = getPortletPhase();
+    boolean isPortletURLSelfReference = false;
+    boolean isStrictXhtmlEncoded = isStrictXhtmlEncoded(url);
+    Bridge.PortletPhase urlType = Bridge.PortletPhase.ACTION_PHASE;
 
     // First check to see if the special URI indicating we should encode
     // a Nonfaces target to just the current portlet (without an associated
@@ -350,7 +342,6 @@ public class PortletExternalContextImpl
     if (isPortletURL(url))
     {
       isPortletURL = true;
-      nonFacesAction = true;
       //URL is of the form scheme:urlType?queryString
       // remove the scheme
       path = url.substring(url.indexOf(":")+ 1);
@@ -381,6 +372,27 @@ public class PortletExternalContextImpl
             + url);
         return url;
       }
+      
+      // We allow use of this syntax to reference another (or this) jsf page --
+      // For example if one wants to create a redisplay link for this page
+      // we recognize its a JSF page because it includes a QS parameter
+      // that references either the viewId or viewPath
+      String s = queryStr.getParameter(Bridge.FACES_VIEW_ID_PARAMETER);
+      String s1 = queryStr.getParameter(Bridge.FACES_VIEW_PATH_PARAMETER);
+      if (s != null && s.equals(Bridge.FACES_USE_CURRENT_VIEW_PARAMETER))
+      {
+        isPortletURLSelfReference = true;
+        // by removing the parameter we will rely on retaining the current view info based on \
+        // the current render params
+        queryStr.removeParameter(Bridge.FACES_VIEW_ID_PARAMETER);
+      }
+      else if (s != null && s1.equals(Bridge.FACES_USE_CURRENT_VIEW_PARAMETER))
+      {
+        isPortletURLSelfReference = true;
+        // by removing the parameter we will rely on retaining the current view info based on \
+        // the current render params
+        queryStr.removeParameter(Bridge.FACES_VIEW_PATH_PARAMETER);
+      }
     }
     else if (url.startsWith("#") || isExternalURL(url) || isDirectLink(url))
     {
@@ -429,7 +441,6 @@ public class PortletExternalContextImpl
       else
       {
         // URL points at non-Faces action
-        nonFacesAction = true;
         encodeNonFacesActionTarget(queryStr, path);
       }
     }
@@ -446,24 +457,17 @@ public class PortletExternalContextImpl
       ResourceURL resourceURL = null;
       BaseURL baseURL = null;
       // Non-JSF actions are renderURLs as we merely dispatch to them
-      if (nonFacesAction)
+      if (urlType == Bridge.PortletPhase.ACTION_PHASE)
       {
-        if (isPortletURL && urlType == Bridge.PortletPhase.ACTION_PHASE)
-        {
-          baseURL = actionURL = mimeResponse.createActionURL();
-        }
-        else if (isPortletURL && urlType == Bridge.PortletPhase.RESOURCE_PHASE)
-        {
-          baseURL = resourceURL = mimeResponse.createResourceURL();
-        }
-        else
-        {
-          baseURL = actionURL = mimeResponse.createRenderURL();
-        }
-      } 
+        baseURL = actionURL = mimeResponse.createActionURL();
+      }
+      else if (urlType == Bridge.PortletPhase.RESOURCE_PHASE)
+      {
+        baseURL = resourceURL = mimeResponse.createResourceURL();
+      }
       else
       {
-        baseURL = actionURL = mimeResponse.createActionURL();
+        baseURL = actionURL = mimeResponse.createRenderURL();
       }
 
       // Add parameters so they don't get lost
@@ -509,14 +513,23 @@ public class PortletExternalContextImpl
           baseURL.setParameter(param, queryStr.getParameter(param));
         }
       }
+      
+      // Carry forward render parameters if this is a portlet:url that references the current view
+      if (isPortletURLSelfReference)
+      {
+        carryForwardRenderParameters(baseURL, mPortletRequest.getPrivateParameterMap(), queryStr);
+        carryForwardRenderParameters(baseURL, mPortletRequest.getPublicParameterMap(), queryStr);
+      }
 
-      // Some portlet containers implementing wsrp choose to separate the 
-      // consumer rewrite string that represents this URL using & as the
-      // spec allows for either that or &.  If the container has chosen to 
-      // to do such -- undo it (use & instead) as faces renderkits/response writers
-      // may reencode when writing XMl content.
-      url = baseURL.toString();
-      url = url.replaceAll("\\&amp\\;", "&");
+      // If we weren't already Xhtml encoded (used &) then renderkit will likely
+      // do it after us if it needs to do such encoding -- so undo any xml encoding the portlet URL might
+      // have done to avoid double encoding -- Otherwise if the string passed in was encoded the renderkit 
+      // is unlikely to reencode after the call -- so don't undo any encoding.
+      if (!isStrictXhtmlEncoded)
+      {
+        url = actionURL.toString();
+        url = url.replaceAll("\\&amp\\;", "&");
+      }
     }
     else if (getPortletPhase() == Bridge.PortletPhase.ACTION_PHASE ||
              getPortletPhase() == Bridge.PortletPhase.EVENT_PHASE)
@@ -579,6 +592,21 @@ public class PortletExternalContextImpl
     
     return url;
   }
+  
+  private void carryForwardRenderParameters(BaseURL url, Map<String, String[]> m, QueryString queryStr)
+  {
+    Set<Map.Entry<String, String[]>> s = m.entrySet();
+    Iterator<Map.Entry<String, String[]>> i = s.iterator();
+    while (i.hasNext())
+    {
+      Map.Entry<String, String[]> entry = i.next();
+      // only add if not already added from above
+      if (queryStr.getParameter(entry.getKey()) == null)
+      {
+        url.setParameter(entry.getKey(), entry.getValue());
+      }
+    }
+  }
 
   @Override
   public void redirect(String url)
@@ -653,7 +681,51 @@ public class PortletExternalContextImpl
   private void redirectDuringRender(String url)
     throws IOException
   {
-    redirectFacesView(url);
+    // As this doesn't go back to the consumer -- we need to reencode the public render parameters so they are there
+    redirectFacesView(encodePublicRenderParameters(url));
+  }
+  
+  private String encodePublicRenderParameters(String inUrl)
+  {
+    // If there are no Public Render Parameters just return the inUrl
+    Map<String, String[]> m = mPortletRequest.getPublicParameterMap();
+    if (m == null || m.isEmpty())
+    {
+      return inUrl;
+    }
+    
+    // Otherwise -- there are public render parameters that need to be encoded
+    StringBuffer outUrl = null;
+    QueryString queryStr = null;
+    int queryStart = inUrl.indexOf('?');
+
+    if (queryStart != -1)
+    {
+      // Get the query string
+      queryStr = new QueryString(inUrl.substring(queryStart + 1), "UTF8");
+      outUrl = new StringBuffer(inUrl.substring(0, queryStart));
+    }
+    else
+    {
+      outUrl = new StringBuffer(inUrl);
+      // construct an empty queryString to hold the viewId
+      queryStr = new QueryString("UTF8");
+    }
+    
+    for (Map.Entry<String, String[]> entry:m.entrySet())
+    {
+      String key = entry.getKey();
+      
+      // Only add if not already reflected in the QS
+      if (queryStr.getParameter(key) == null)
+      {
+        for (String param:entry.getValue())
+        {
+          queryStr.addParameter(key, param);
+        }  
+      }
+    }
+    return outUrl.append("?").append(queryStr.toString()).toString();
   }
   
   private void redirectFacesView(String url)
@@ -704,28 +776,39 @@ public class PortletExternalContextImpl
   @Override
   public String encodeResourceURL(String s)
   {
-    if (isOpaqueURL(s))
+    if (isPortletURL(s))
+    {
+      return urlEncode(encodeResourceAsViewNavigationURL(s));
+    }
+    else if (isOpaqueURL(s))
     {
-        return s;
+      // spec says return this unchanged
+      return s;
     }
-      if (isExternalURL(s))
+    else if (isExternalURL(s))
     {
-      return encodeResourceURL(s, false); 
+      return urlEncode(encodeResourceURL(s, false)); 
     }
-    else if (isViewLink(s) || isPortletURL(s))
+    else if (isViewLink(s))
     {
-      return encodeResourceAsViewNavigationURL(s);
+      return urlEncode(encodeResourceAsViewNavigationURL(s));
     }
     else if (!isInProtocolResourceLink(s))
     {
-      return encodeResourceURL(s, true); 
+      return urlEncode(encodeResourceURL(s, true)); 
     }
     else
     {
-      return encodePortletServedResourceURL(s);
+      return urlEncode(encodePortletServedResourceURL(s));
     }
   }
   
+  private String urlEncode(String s)
+  {
+    // replace all spaces in the url with %20 to avoid Faces from encoding with a +
+    return s.replace(" ", "%20");
+  }
+  
   private String encodeResourceURL(String s, boolean targetInApp)
   {
     if (targetInApp)
@@ -735,13 +818,25 @@ public class PortletExternalContextImpl
     
     // Check for backlink and viewlink markers -- if they exist replace
     s = replaceResourceQueryStringMarkers(s);
+
+    // If we weren't already Xhtml encoded (used &amp;) then renderkit will likely
+    // do it after us if it needs to do such encoding -- so undo any xml encoding the portlet URL might
+    // have done to avoid double encoding -- Otherwise if the string passed in was encoded the renderkit 
+    // is unlikely to reencode after the call -- so don't undo any encoding.
+    if (!isStrictXhtmlEncoded(s))
+    {
     
-    // Some portlet containers implementing wsrp choose to separate the 
-    // consumer rewrite string that represents this URL using &amp; as the
-    // spec allows for either that or &.  If the container has chosen to 
-    // to do such -- undo it (use & instead) as faces renderkits/response writers
-    // may reencode when writing XMl content.
-    return mPortletResponse.encodeURL(s).replaceAll("\\&amp\\;", "&");
+      // Some portlet containers implementing wsrp choose to separate the 
+      // consumer rewrite string that represents this URL using &amp; as the
+      // spec allows for either that or &.  If the container has chosen to 
+      // to do such -- undo it (use & instead) as faces renderkits/response writers
+      // may reencode when writing XMl content.
+      return mPortletResponse.encodeURL(s).replaceAll("\\&amp\\;", "&");
+    }
+    else
+    {
+      return mPortletResponse.encodeURL(s); 
+    }
   }
   
   private String normalizeResourcePath(String s)
@@ -1542,6 +1637,17 @@ public class PortletExternalContextImpl
       
   }
 
+  @Override
+  public Writer getResponseOutputWriter() throws IOException
+  {
+    if (mPortletResponse instanceof MimeResponse)
+    {
+      return ((MimeResponse)mPortletResponse).getWriter();
+    }
+    
+    throw new IllegalStateException();
+  }
+
   /**
    * <p>
    * Set the environment-specific response to be returned by subsequent calls to
@@ -1592,9 +1698,42 @@ public class PortletExternalContextImpl
   {
     // JSR 168 has no corresponding API.
   }
+  
+  @Override
+  public boolean isResponseCommitted()
+  {
+    if(mPortletResponse instanceof MimeResponse)
+    {
+      return ((MimeResponse)mPortletResponse).isCommitted();
+    }
+    
+    //TODO: What should be returned here?  False or Unsupported Operation?
+    throw new IllegalStateException();
+  }
 
-  // End of JSF 1.2 API
+  @Override
+  public void responseReset()
+  {
+    if(mPortletResponse instanceof MimeResponse)
+    {
+      ((MimeResponse)mPortletResponse).reset();
+    }
+    else
+    {
+      //TODO: What should be done here?  noop or UnsupportedOperationException?
+      throw new IllegalStateException();
+    }
+  }
+
+  @Override
+  public void setResponseStatus(int statusCode)
+  {
+    //TODO: What should I do here.  It would be cool to render out an
+    //error page to display in the portlet
+    System.out.println("*****Status Code: "+ statusCode);
+  }
 
+  // End of JSF 1.2 API
   private Bridge.PortletPhase getPortletPhase()
   {
     return (Bridge.PortletPhase) getRequestMap().get(Bridge.PORTLET_LIFECYCLE_PHASE);
@@ -2067,6 +2206,33 @@ public class PortletExternalContextImpl
     return url.toLowerCase().startsWith("portlet:");
   }
 
+  private boolean isStrictXhtmlEncoded(String url)
+  {
+    // check for use of &amp; in query string
+    int currentPos = url.indexOf('?');
+
+    if (currentPos == -1) return false;
+    
+    boolean isStrict = false;    
+    while (true)
+    {
+      int ampPos = url.indexOf('&', currentPos);
+      int xhtmlAmpPos = url.indexOf("\\&amp\\;", currentPos);
+      // no more & to process -- so return current value of isStrict
+      if (ampPos == -1)
+      {
+        return isStrict;
+      }
+      // if the amp we found doesn't start an &amp; then its not strict
+      if (ampPos != xhtmlAmpPos)
+      {
+        return false;
+      }
+      isStrict = true;
+      currentPos = ampPos;
+    }
+  }
+
 
   private boolean isAbsoluteURL(String url)
   {
@@ -2110,8 +2276,8 @@ public class PortletExternalContextImpl
     }
     
     // Its opaque if it is absolute (contains a scheme) that isn't followed
-    // by a '/'
-    return (url.indexOf(':') != (url.indexOf('/') - 1));
+    // by a '/' or doesn't start with our special portlet: scheme
+    return (!isPortletURL(url) && (url.indexOf(':') != (url.indexOf('/') - 1)));
   }
 
   private boolean isExternalURL(String url)