MYFACES-3816
diff --git a/api/src/main/java/javax/faces/lifecycle/ClientWindow.java b/api/src/main/java/javax/faces/lifecycle/ClientWindow.java
index 0171f51..31d343e 100644
--- a/api/src/main/java/javax/faces/lifecycle/ClientWindow.java
+++ b/api/src/main/java/javax/faces/lifecycle/ClientWindow.java
@@ -29,8 +29,12 @@
{
/**
* Defines the ClientWindow mode to use.
+ * url = like the defined in the specs
+ * url-redirect = same like 'url' but with a initial redirect, so that the first request already contains
+ * a valid windowId in the URL. Similar to DeltaSpile LAZY mode.
+ * client = like the DeltaSpike CLIENTWINDOW mode.
*/
- @JSFWebConfigParam(since = "2.2.0", expectedValues = "none, url, client", defaultValue = "none")
+ @JSFWebConfigParam(since = "2.2.0", expectedValues = "none, url, url-redirect, client", defaultValue = "none")
public static final String CLIENT_WINDOW_MODE_PARAM_NAME =
"javax.faces.CLIENT_WINDOW_MODE";
diff --git a/impl/src/main/java/org/apache/myfaces/lifecycle/LifecycleImpl.java b/impl/src/main/java/org/apache/myfaces/lifecycle/LifecycleImpl.java
index 73ffa98..7c05715 100755
--- a/impl/src/main/java/org/apache/myfaces/lifecycle/LifecycleImpl.java
+++ b/impl/src/main/java/org/apache/myfaces/lifecycle/LifecycleImpl.java
@@ -93,8 +93,16 @@
}
if (clientWindow != null)
{
- clientWindow.decode(facesContext);
- facesContext.getExternalContext().setClientWindow(clientWindow);
+ try
+ {
+ facesContext.getExternalContext().setClientWindow(clientWindow);
+ clientWindow.decode(facesContext);
+ }
+ catch (RuntimeException e)
+ {
+ facesContext.getExternalContext().setClientWindow(null);
+ throw e;
+ }
}
}
diff --git a/impl/src/main/java/org/apache/myfaces/lifecycle/clientwindow/ClientWindowFactoryImpl.java b/impl/src/main/java/org/apache/myfaces/lifecycle/clientwindow/ClientWindowFactoryImpl.java
index 356d231..13d7f82 100644
--- a/impl/src/main/java/org/apache/myfaces/lifecycle/clientwindow/ClientWindowFactoryImpl.java
+++ b/impl/src/main/java/org/apache/myfaces/lifecycle/clientwindow/ClientWindowFactoryImpl.java
@@ -39,6 +39,7 @@
public static final String WINDOW_MODE_NONE = "none";
public static final String WINDOW_MODE_URL = "url";
+ public static final String WINDOW_MODE_URL_REDIRECT = "url-redirect";
public static final String WINDOW_MODE_CLIENT = "client";
private String windowMode;
@@ -67,6 +68,10 @@
{
return new UrlClientWindow(windowTokenGenerator);
}
+ if (WINDOW_MODE_URL_REDIRECT.equals(getWindowMode(facesContext)))
+ {
+ return new UrlRedirectClientWindow(windowTokenGenerator);
+ }
else if (WINDOW_MODE_CLIENT.equals(getWindowMode(facesContext)))
{
return new CODIClientSideWindow(windowTokenGenerator, windowContextConfig, clientConfig);
diff --git a/impl/src/main/java/org/apache/myfaces/lifecycle/clientwindow/UrlClientWindow.java b/impl/src/main/java/org/apache/myfaces/lifecycle/clientwindow/UrlClientWindow.java
index 20afb30..0d6f486 100644
--- a/impl/src/main/java/org/apache/myfaces/lifecycle/clientwindow/UrlClientWindow.java
+++ b/impl/src/main/java/org/apache/myfaces/lifecycle/clientwindow/UrlClientWindow.java
@@ -31,11 +31,10 @@
*/
public class UrlClientWindow extends ClientWindow
{
- private String windowId;
+ protected TokenGenerator tokenGenerator;
- private TokenGenerator tokenGenerator;
-
- private Map<String,String> queryParamsMap;
+ private String windowId;
+ private Map<String, String> queryParamsMap;
public UrlClientWindow(TokenGenerator tokenGenerator)
{
@@ -45,37 +44,23 @@
@Override
public void decode(FacesContext context)
{
- String windowId = calculateWindowId(context);
-
- if (windowId != null)
- {
- // Store the current windowId.
- setId(windowId);
- }
- else
- {
- //Generate a new windowId
- setId(tokenGenerator.getNextToken());
- }
- }
-
- protected String calculateWindowId(FacesContext context)
- {
//1. If it comes as parameter, it takes precedence over any other choice, because
// no browser is capable to do a POST and create a new window at the same time.
- String windowId = context.getExternalContext().getRequestParameterMap().get(
+ String requestWindowId = context.getExternalContext().getRequestParameterMap().get(
ResponseStateManager.CLIENT_WINDOW_PARAM);
- if (windowId != null)
+
+ if (requestWindowId == null)
{
- return windowId;
+ requestWindowId = context.getExternalContext().getRequestParameterMap().get(
+ ResponseStateManager.CLIENT_WINDOW_URL_PARAM);
}
- windowId = context.getExternalContext().getRequestParameterMap().get(
- ResponseStateManager.CLIENT_WINDOW_URL_PARAM);
- if (windowId != null)
+
+ if (requestWindowId == null)
{
- return windowId;
+ requestWindowId = tokenGenerator.getNextToken();
}
- return null;
+
+ setId(requestWindowId);
}
@Override
@@ -86,8 +71,8 @@
public void setId(String id)
{
- windowId = id;
- queryParamsMap = null;
+ this.windowId = id;
+ this.queryParamsMap = null;
}
@Override
diff --git a/impl/src/main/java/org/apache/myfaces/lifecycle/clientwindow/UrlRedirectClientWindow.java b/impl/src/main/java/org/apache/myfaces/lifecycle/clientwindow/UrlRedirectClientWindow.java
new file mode 100644
index 0000000..df077c8
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/lifecycle/clientwindow/UrlRedirectClientWindow.java
@@ -0,0 +1,153 @@
+/*
+ * 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.lifecycle.clientwindow;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Map;
+import javax.faces.FacesException;
+import javax.faces.context.ExternalContext;
+import org.apache.myfaces.util.token.TokenGenerator;
+import javax.faces.context.FacesContext;
+import javax.faces.render.ResponseStateManager;
+import org.apache.myfaces.util.lang.StringUtils;
+
+public class UrlRedirectClientWindow extends UrlClientWindow
+{
+
+ public UrlRedirectClientWindow(TokenGenerator tokenGenerator)
+ {
+ super(tokenGenerator);
+ }
+
+ @Override
+ public void decode(FacesContext context)
+ {
+ //1. If it comes as parameter, it takes precedence over any other choice, because
+ // no browser is capable to do a POST and create a new window at the same time.
+ String requestWindowId = context.getExternalContext().getRequestParameterMap().get(
+ ResponseStateManager.CLIENT_WINDOW_PARAM);
+
+ if (requestWindowId == null)
+ {
+ requestWindowId = context.getExternalContext().getRequestParameterMap().get(
+ ResponseStateManager.CLIENT_WINDOW_URL_PARAM);
+ }
+
+ if (requestWindowId == null)
+ {
+ requestWindowId = tokenGenerator.getNextToken();
+ setId(requestWindowId);
+
+ try
+ {
+ // this will also include the new generated windowId
+ String redirectUrl = constructInitialRedirectUrl(context.getExternalContext());
+ context.getExternalContext().redirect(redirectUrl);
+ }
+ catch (IOException e)
+ {
+ throw new FacesException("Could not send initial redirect!", e);
+ }
+
+ context.responseComplete();
+ }
+
+ setId(requestWindowId);
+ }
+
+ protected String constructInitialRedirectUrl(ExternalContext externalContext)
+ {
+ String url = externalContext.getRequestContextPath() + externalContext.getRequestServletPath();
+ if (externalContext.getRequestPathInfo() != null)
+ {
+ url += externalContext.getRequestPathInfo();
+ }
+
+ url = addRequestParameters(externalContext, url);
+ url = externalContext.encodeRedirectURL(url, null);
+
+ return url;
+ }
+
+ public static String addRequestParameters(ExternalContext externalContext, String url)
+ {
+ if (externalContext.getRequestParameterValuesMap().isEmpty())
+ {
+ return url;
+ }
+
+ StringBuilder finalUrl = new StringBuilder(url);
+ boolean existingParameters = url.contains("?");
+
+ for (Map.Entry<String, String[]> entry : externalContext.getRequestParameterValuesMap().entrySet())
+ {
+ for (String value : entry.getValue())
+ {
+ if (!url.contains(entry.getKey() + "=" + value) &&
+ !url.contains(entry.getKey() + "=" + encodeURLParameterValue(value, externalContext)))
+ {
+ if (StringUtils.isEmpty(entry.getKey()) && StringUtils.isEmpty(value))
+ {
+ continue;
+ }
+
+ if (!existingParameters)
+ {
+ finalUrl.append("?");
+ existingParameters = true;
+ }
+ else
+ {
+ finalUrl.append("&");
+ }
+
+ finalUrl.append(encodeURLParameterValue(entry.getKey(), externalContext));
+ finalUrl.append("=");
+ finalUrl.append(encodeURLParameterValue(value, externalContext));
+ }
+ }
+ }
+
+ return finalUrl.toString();
+ }
+
+ /**
+ * Encodes the given value using URLEncoder.encode() with the charset returned
+ * from ExternalContext.getResponseCharacterEncoding().
+ * This is exactly how the ExternalContext impl encodes URL parameter values.
+ *
+ * @param value value which should be encoded
+ * @param externalContext current external-context
+ * @return encoded value
+ */
+ public static String encodeURLParameterValue(String value, ExternalContext externalContext)
+ {
+ try
+ {
+ return URLEncoder.encode(value, externalContext.getResponseCharacterEncoding());
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new UnsupportedOperationException("Encoding type="
+ + externalContext.getResponseCharacterEncoding() + " not supported", e);
+ }
+ }
+}