You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ja...@apache.org on 2010/11/26 21:59:18 UTC

svn commit: r1039559 - in /myfaces/extensions/cdi/trunk: examples/jsf-examples/windowhandler_jsf20/src/main/webapp/WEB-INF/ jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/scope/conversation/ jee-modules/jsf20-modul...

Author: jakobk
Date: Fri Nov 26 20:59:18 2010
New Revision: 1039559

URL: http://svn.apache.org/viewvc?rev=1039559&view=rev
Log:
EXTCDI-79 change WindowHandlerPhaseListener to ClientSideWindowHandler using new LifecycleAwareWindowHandler SPI (+ added TODOs and removed unused WindowHandlerServlet)

Added:
    myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientInformation.java
      - copied, changed from r1039532, myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/windowhandler/ClientInformation.java
    myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientSideWindowHandler.java
      - copied, changed from r1039532, myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientAwareWindowHandler.java
    myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/spi/LifecycleAwareWindowHandler.java
Removed:
    myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientAwareWindowHandler.java
    myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/windowhandler/
Modified:
    myfaces/extensions/cdi/trunk/examples/jsf-examples/windowhandler_jsf20/src/main/webapp/WEB-INF/beans.xml
    myfaces/extensions/cdi/trunk/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/scope/conversation/DefaultWindowHandler.java
    myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/listener/phase/CodiLifecycleWrapper.java

Modified: myfaces/extensions/cdi/trunk/examples/jsf-examples/windowhandler_jsf20/src/main/webapp/WEB-INF/beans.xml
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/examples/jsf-examples/windowhandler_jsf20/src/main/webapp/WEB-INF/beans.xml?rev=1039559&r1=1039558&r2=1039559&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/examples/jsf-examples/windowhandler_jsf20/src/main/webapp/WEB-INF/beans.xml (original)
+++ myfaces/extensions/cdi/trunk/examples/jsf-examples/windowhandler_jsf20/src/main/webapp/WEB-INF/beans.xml Fri Nov 26 20:59:18 2010
@@ -21,4 +21,8 @@
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
 
+    <alternatives>
+        <class>org.apache.myfaces.extensions.cdi.jsf2.impl.scope.conversation.ClientSideWindowHandler</class>
+    </alternatives>
+
 </beans>
\ No newline at end of file

Modified: myfaces/extensions/cdi/trunk/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/scope/conversation/DefaultWindowHandler.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/scope/conversation/DefaultWindowHandler.java?rev=1039559&r1=1039558&r2=1039559&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/scope/conversation/DefaultWindowHandler.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/scope/conversation/DefaultWindowHandler.java Fri Nov 26 20:59:18 2010
@@ -99,6 +99,9 @@ public class DefaultWindowHandler implem
                 finalUrl.append("&");
                 finalUrl.append(key);
                 finalUrl.append("=");
+                // TODO encodeActionURL does NOT correctly encode URL parameter values
+                // see MyFaces ServletExternalContextImpl.encodeURL() for detail!
+                // TODO use ClientSideWindowHandler.encodeURIComponent() instead
                 finalUrl.append(externalContext.encodeActionURL(requestParam.getValue()));
             }
         }

Modified: myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/listener/phase/CodiLifecycleWrapper.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/listener/phase/CodiLifecycleWrapper.java?rev=1039559&r1=1039558&r2=1039559&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/listener/phase/CodiLifecycleWrapper.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/listener/phase/CodiLifecycleWrapper.java Fri Nov 26 20:59:18 2010
@@ -18,6 +18,10 @@
  */
 package org.apache.myfaces.extensions.cdi.jsf2.impl.listener.phase;
 
+import org.apache.myfaces.extensions.cdi.core.impl.util.CodiUtils;
+import org.apache.myfaces.extensions.cdi.jsf.impl.scope.conversation.spi.WindowHandler;
+import org.apache.myfaces.extensions.cdi.jsf2.impl.scope.conversation.spi.LifecycleAwareWindowHandler;
+
 import javax.faces.FacesException;
 import javax.faces.context.FacesContext;
 import javax.faces.event.PhaseListener;
@@ -52,7 +56,16 @@ class CodiLifecycleWrapper extends Lifec
     public void execute(FacesContext facesContext)
             throws FacesException
     {
-        // TODO move WindowHandlerPhaseListener invocation here (as early as possible, before any other PhaseListener) ?
+        WindowHandler windowHandler = CodiUtils.getOrCreateScopedInstanceOfBeanByClass(WindowHandler.class, false);
+        if (windowHandler instanceof LifecycleAwareWindowHandler)
+        {
+            ((LifecycleAwareWindowHandler) windowHandler).beforeLifecycleExecute(facesContext);
+            if (facesContext.getResponseComplete())
+            {
+                // no further processing
+                return;
+            }
+        }
 
         wrapped.execute(facesContext);
     }

Copied: myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientInformation.java (from r1039532, myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/windowhandler/ClientInformation.java)
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientInformation.java?p2=myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientInformation.java&p1=myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/windowhandler/ClientInformation.java&r1=1039532&r2=1039559&rev=1039559&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/windowhandler/ClientInformation.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientInformation.java Fri Nov 26 20:59:18 2010
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.myfaces.extensions.cdi.jsf2.impl.windowhandler;
+package org.apache.myfaces.extensions.cdi.jsf2.impl.scope.conversation;
 
 import org.apache.myfaces.extensions.cdi.core.api.projectstage.ProjectStage;
 import org.apache.myfaces.extensions.cdi.core.api.util.ClassUtils;

Copied: myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientSideWindowHandler.java (from r1039532, myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientAwareWindowHandler.java)
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientSideWindowHandler.java?p2=myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientSideWindowHandler.java&p1=myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientAwareWindowHandler.java&r1=1039532&r2=1039559&rev=1039559&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientAwareWindowHandler.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/ClientSideWindowHandler.java Fri Nov 26 20:59:18 2010
@@ -18,71 +18,252 @@
  */
 package org.apache.myfaces.extensions.cdi.jsf2.impl.scope.conversation;
 
-import org.apache.myfaces.extensions.cdi.jsf.impl.scope.conversation.DefaultWindowHandler;
-import org.apache.myfaces.extensions.cdi.jsf2.impl.windowhandler.Jsf2WindowHandlerServlet;
 import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.config.WindowContextConfig;
+import org.apache.myfaces.extensions.cdi.jsf.impl.scope.conversation.DefaultWindowHandler;
+import org.apache.myfaces.extensions.cdi.jsf.impl.scope.conversation.spi.EditableWindowContext;
+import org.apache.myfaces.extensions.cdi.jsf.impl.scope.conversation.spi.EditableWindowContextManager;
+import org.apache.myfaces.extensions.cdi.jsf2.impl.scope.conversation.spi.LifecycleAwareWindowHandler;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.inject.Alternative;
+import javax.faces.FacesException;
 import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
-import javax.faces.context.PartialViewContext;
 import javax.inject.Inject;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import static org.apache.myfaces.extensions.cdi.core.impl.scope.conversation.spi.WindowContextManager
+        .CREATE_NEW_WINDOW_CONTEXT_ID_VALUE;
+import static org.apache.myfaces.extensions.cdi.core.impl.scope.conversation.spi.WindowContextManager
+        .WINDOW_CONTEXT_ID_PARAMETER_KEY;
 
 /**
- * WindowHandler with JSF2 features
+ * WindowHandler which uses JavaScript to store the windowId.
  */
 @Alternative
 @ApplicationScoped
-public class ClientAwareWindowHandler extends DefaultWindowHandler
+public class ClientSideWindowHandler extends DefaultWindowHandler implements LifecycleAwareWindowHandler
 {
     private static final long serialVersionUID = 5293942986187078113L;
 
-    protected ClientAwareWindowHandler()
+    private static final String WINDOW_ID_COOKIE_SUFFIX = "-codiWindowId";
+    private static final String UNINITIALIZED_WINDOW_ID_VALUE = "uninitializedWindowId";
+    private static final String WINDOW_ID_REPLACE_PATTERN = "$$windowIdValue$$";
+
+    @Inject
+    private ClientInformation clientInformation;
+
+    @Inject
+    private EditableWindowContextManager windowContextManager;
+
+    protected ClientSideWindowHandler()
     {
-        // default ct is needed for proxying 
+        // needed for proxying
     }
 
     @Inject
-    protected ClientAwareWindowHandler(WindowContextConfig config)
+    protected ClientSideWindowHandler(WindowContextConfig config)
     {
         super(config);
     }
 
     @Override
-    public void sendRedirect(ExternalContext externalContext, String url, boolean addRequestParameter)
-            throws IOException
+    public String encodeURL(String url)
     {
-        PartialViewContext partialViewContext = FacesContext.getCurrentInstance().getPartialViewContext();
+        // do NOT add the windowId to the URL in any case
+        // TODO what if javascript is disabled? fallback to default algorithm?
+        return url;
+    }
 
-        if (partialViewContext != null && partialViewContext.isPartialRequest())
+    @Override
+    public String restoreWindowId(ExternalContext externalContext)
+    {
+        return null;
+    }
+
+    public void beforeLifecycleExecute(FacesContext facesContext)
+    {
+        if (!shouldHandleRequest(facesContext))
         {
-            super.sendRedirect(externalContext, url, addRequestParameter);
             return;
         }
 
-        if (url != null && url.startsWith(Jsf2WindowHandlerServlet.WINDOWHANDLER_URL))
+        ExternalContext externalContext = facesContext.getExternalContext();
+
+        String windowId = getWindowIdFromCookie(externalContext);
+        if (windowId == null)
         {
-            externalContext.redirect(url);
-            return;
+            // GET request without windowId - send windowhandlerfilter.html
+            sendWindowHandlerHtml(externalContext, null);
+            facesContext.responseComplete();
+        }
+        else
+        {
+            if (CREATE_NEW_WINDOW_CONTEXT_ID_VALUE.equals(windowId) || !isWindowIdAlive(windowId))
+            {
+                // no or invalid windowId --> create new one
+                windowId = windowContextManager.getCurrentWindowContext().getId();
+
+                // GET request with NEW windowId - send windowhandlerfilter.html
+                sendWindowHandlerHtml(externalContext, windowId);
+                facesContext.responseComplete();
+            }
+            else
+            {
+                // we have a valid windowId - set it and continue with the request
+
+                //X TODO find better way to provide the windowId, because this approach assumes
+                // that the windowId will be cached on the RequestMap and the cache is the only
+                // point to get it #HACK
+                externalContext.getRequestMap().put(WINDOW_CONTEXT_ID_PARAMETER_KEY, windowId);
+            }
+        }
+    }
+
+    private boolean shouldHandleRequest(FacesContext facesContext)
+    {
+        // no POST request and javascript enabled
+        // NOTE that for POST-requests the windowId is saved in the state (see WindowContextIdHolderComponent)
+        return !facesContext.isPostback() && clientInformation.isJavaScriptEnabled();
+    }
+
+    private void sendWindowHandlerHtml(ExternalContext externalContext, String windowId)
+    {
+        HttpServletResponse httpResponse = (HttpServletResponse) externalContext.getResponse();
+
+        try
+        {
+            httpResponse.setStatus(HttpServletResponse.SC_OK);
+            httpResponse.setContentType("text/html");
+
+            String windowHandlerHtml = clientInformation.getWindowHandlerHtml();
+
+            if (windowId == null)
+            {
+                windowId = UNINITIALIZED_WINDOW_ID_VALUE;
+            }
+
+            // set the windowId value in the javascript code
+            windowHandlerHtml = windowHandlerHtml.replace(WINDOW_ID_REPLACE_PATTERN, windowId);
+
+            OutputStream os = httpResponse.getOutputStream();
+            try
+            {
+                    os.write(windowHandlerHtml.getBytes());
+            }
+            finally
+            {
+                os.close();
+            }
+        }
+        catch (IOException ioe)
+        {
+            throw new FacesException(ioe);
+        }
+    }
+
+    private String getWindowIdFromCookie(ExternalContext externalContext)
+    {
+        String cookieName = getEncodedPathName(externalContext) + WINDOW_ID_COOKIE_SUFFIX;
+        Cookie cookie = (Cookie) externalContext.getRequestCookieMap().get(cookieName);
+
+        if (cookie == null)
+        {
+            // if the current request went to a welcome page, we should only consider the contextPath
+            cookieName = getEncodedContextPath(externalContext) + WINDOW_ID_COOKIE_SUFFIX;
+            cookie = (Cookie) externalContext.getRequestCookieMap().get(cookieName);
         }
 
-        super.sendRedirect(externalContext, url, addRequestParameter);
+        if (cookie != null)
+        {
+            return cookie.getValue();
+        }
+
+        return null;
     }
 
-    private String getWindowHandlerPath(ExternalContext externalContext)
+    private String getEncodedPathName(ExternalContext externalContext)
     {
+        StringBuilder sb = new StringBuilder();
+
         String contextPath = externalContext.getRequestContextPath();
+        if (contextPath != null)
+        {
+            sb.append(contextPath);
+        }
 
-        if (contextPath == null)
+        String servletPath = externalContext.getRequestServletPath();
+        if (servletPath != null)
         {
-            return Jsf2WindowHandlerServlet.WINDOWHANDLER_URL;
+            sb.append(servletPath);
         }
-        else
+
+        String pathInfo = externalContext.getRequestPathInfo();
+        if (pathInfo != null)
+        {
+            sb.append(pathInfo);
+        }
+
+        // remove all "/", because they can be different in JavaScript
+        String pathName = sb.toString().replace("/", "");
+
+        return encodeURIComponent(pathName, externalContext);
+    }
+
+    private String getEncodedContextPath(ExternalContext externalContext)
+    {
+        String contextPath = externalContext.getRequestContextPath();
+        if (contextPath != null)
+        {
+            // remove all "/", because they can be different in JavaScript
+            contextPath = contextPath.replace("/", "");
+
+            return encodeURIComponent(contextPath, externalContext);
+        }
+
+        return "";
+    }
+
+    /**
+     * JavaScript equivalent method.
+     *
+     * This is how the ExternalContext impl encodes URL parameter values.
+     *
+     * TODO move to Utils class - also needed in DefaultWindowHandler
+     *
+     * @param component
+     * @param externalContext
+     * @return
+     */
+    private String encodeURIComponent(String component, ExternalContext externalContext)
+    {
+        try
         {
-            return contextPath + Jsf2WindowHandlerServlet.WINDOWHANDLER_URL;
+            return URLEncoder.encode(component, externalContext.getResponseCharacterEncoding());
         }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new UnsupportedOperationException("Encoding type="
+                    + externalContext.getResponseCharacterEncoding() + " not supported", e);
+        }
+    }
+
+    private boolean isWindowIdAlive(String windowId)
+    {
+        for (EditableWindowContext wc : windowContextManager.getWindowContexts())
+        {
+            if (windowId.equals(wc.getId()))
+            {
+                return true;
+            }
+        }
+
+        return false;
     }
 
 }

Added: myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/spi/LifecycleAwareWindowHandler.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/spi/LifecycleAwareWindowHandler.java?rev=1039559&view=auto
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/spi/LifecycleAwareWindowHandler.java (added)
+++ myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf2/impl/scope/conversation/spi/LifecycleAwareWindowHandler.java Fri Nov 26 20:59:18 2010
@@ -0,0 +1,41 @@
+/*
+ * 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.extensions.cdi.jsf2.impl.scope.conversation.spi;
+
+import org.apache.myfaces.extensions.cdi.jsf.impl.scope.conversation.spi.WindowHandler;
+
+import javax.faces.context.FacesContext;
+
+/**
+ *  A WindowHandler that is aware of the JSF lifecycle.
+ *
+ * @author Jakob Korherr
+ */
+public interface LifecycleAwareWindowHandler extends WindowHandler
+{
+
+    /**
+     * Is called before the execute portion of the JSF lifecycle starts.
+     * If this method sets responseComplete() to true, the lifecycle won't be started.
+     *
+     * @param facesContext
+     */
+    void beforeLifecycleExecute(FacesContext facesContext);
+
+}
\ No newline at end of file