You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltaspike.apache.org by st...@apache.org on 2013/04/21 21:46:15 UTC
[1/3] git commit: DELTASPIKE-289 implement ClientWindowConfig
Updated Branches:
refs/heads/master ac93bc54a -> 45770596d
DELTASPIKE-289 implement ClientWindowConfig
This can be used to determine the window handling behaviour
for each request.
Project: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/commit/ff5df7d3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/tree/ff5df7d3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/diff/ff5df7d3
Branch: refs/heads/master
Commit: ff5df7d320c9819754586497eb36a07a2d384652
Parents: ac93bc5
Author: Mark Struberg <st...@apache.org>
Authored: Sun Apr 21 10:54:54 2013 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Sun Apr 21 10:54:54 2013 +0200
----------------------------------------------------------------------
.../core/impl/scope/window/WindowBeanHolder.java | 13 +-
.../core/impl/scope/window/WindowContextImpl.java | 4 +-
deltaspike/modules/jsf/api/pom.xml | 11 +
.../jsf/spi/window/ClientWindowConfig.java | 79 ++++++
.../jsf/spi/window/DefaultClientWindowConfig.java | 215 +++++++++++++++
5 files changed, 315 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/ff5df7d3/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowBeanHolder.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowBeanHolder.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowBeanHolder.java
index 8acd0e6..b4acc46 100644
--- a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowBeanHolder.java
+++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowBeanHolder.java
@@ -43,12 +43,6 @@ public class WindowBeanHolder implements Serializable
*/
private volatile Map<String, ContextualStorage> storageMap = new ConcurrentHashMap<String, ContextualStorage>();
- //X TODO review usage
- public Map<String, ContextualStorage> getStorageMap()
- {
- return storageMap;
- }
-
/**
* This method will return the ContextualStorage or create a new one
* if no one is yet assigned to the current windowId.
@@ -90,6 +84,13 @@ public class WindowBeanHolder implements Serializable
return oldStorageMap;
}
+ /**
+ * This method properly destroys all current @WindowScoped beans
+ * of the active session and also prepares the storage for new beans.
+ * It will automatically get called when the session context closes
+ * but can also get invoked manually, e.g. if a user likes to get rid
+ * of all it's @WindowScoped beans.
+ */
@PreDestroy
public void destroyBeans()
{
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/ff5df7d3/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowContextImpl.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowContextImpl.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowContextImpl.java
index e9aa326..ebda229 100644
--- a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowContextImpl.java
+++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowContextImpl.java
@@ -30,7 +30,9 @@ import org.apache.deltaspike.core.util.context.AbstractContext;
import org.apache.deltaspike.core.util.context.ContextualStorage;
/**
- * Context to handle @{@link WindowScoped} beans.
+ * CDI Context to handle @{@link WindowScoped} beans.
+ * This also implements the interface to control the id of
+ * the currently active 'window' (e.g. a web browser tab).
*/
@Typed()
public class WindowContextImpl extends AbstractContext implements WindowContext
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/ff5df7d3/deltaspike/modules/jsf/api/pom.xml
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/pom.xml b/deltaspike/modules/jsf/api/pom.xml
index cdc9c63..874148c 100644
--- a/deltaspike/modules/jsf/api/pom.xml
+++ b/deltaspike/modules/jsf/api/pom.xml
@@ -36,6 +36,17 @@
<groupId>org.apache.deltaspike.core</groupId>
<artifactId>deltaspike-core-api</artifactId>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_2.5_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.myfaces.core</groupId>
+ <artifactId>myfaces-api</artifactId>
+ </dependency>
+
+
</dependencies>
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/ff5df7d3/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/ClientWindowConfig.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/ClientWindowConfig.java b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/ClientWindowConfig.java
new file mode 100644
index 0000000..94690ec
--- /dev/null
+++ b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/ClientWindowConfig.java
@@ -0,0 +1,79 @@
+/*
+ * 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.deltaspike.jsf.spi.window;
+
+import javax.faces.context.FacesContext;
+
+/**
+ * Configuration for ClientWindow handler which is used
+ * to determine the correct windowId for ?WindowScoped beans.
+ */
+public interface ClientWindowConfig
+{
+ public enum ClientWindowRenderMode
+ {
+ /**
+ * Any window or browser tab detection is disabled for this request
+ */
+ NONE,
+
+ /**
+ * <p>The GET request results in an intermediate small html page which
+ * checks if the browser tab fits the selected windowId</p>
+ * <p>The ClientWindow html extracts the windowId from the window.name and
+ * enforces a 2nd GET which will contain the windowId and will get routed
+ * through to the target JSF page.</p>
+ */
+ CLIENTWINDOW,
+
+ /**
+ * Render each GET request with the windowId you get during the request
+ * and perform a lazy check on the client side via JavaScript or similar.
+ */
+ LAZY
+
+ }
+
+ /**
+ * @return whether JavaScript is enabled
+ */
+ boolean isJavaScriptEnabled();
+
+ /**
+ * @param javaScriptEnabled whether JavaScript is enabled
+ */
+ void setJavaScriptEnabled(boolean javaScriptEnabled);
+
+ /**
+ * Determine whether this request should take care of clientWindow detection.
+ * This can e.g. get disabled for download pages or if a useragent doesn't
+ * support html5 or any other required technique.
+ * This only gets checked for GET requests!
+ *
+ * @param facesContext
+ * @return the selected ClientWindowRenderMode
+ */
+ ClientWindowRenderMode getClientWindowRenderMode(FacesContext facesContext);
+
+ /**
+ * @return the prepared html which gets sent out to the client as intermediate client window.
+ */
+ String getClientWindowHtml();
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/ff5df7d3/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/DefaultClientWindowConfig.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/DefaultClientWindowConfig.java b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/DefaultClientWindowConfig.java
new file mode 100644
index 0000000..dbec591
--- /dev/null
+++ b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/DefaultClientWindowConfig.java
@@ -0,0 +1,215 @@
+/*
+ * 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.deltaspike.jsf.spi.window;
+
+import javax.enterprise.context.SessionScoped;
+import javax.faces.context.FacesContext;
+import javax.inject.Inject;
+import javax.servlet.http.Cookie;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.Map;
+
+import org.apache.deltaspike.core.api.projectstage.ProjectStage;
+import org.apache.deltaspike.core.util.ClassUtils;
+import org.apache.deltaspike.core.util.ExceptionUtils;
+
+/**
+ * Default implementation of {@link ClientWindowConfig}.
+ * It will use the internal <code>windowhandler.html</code>
+ */
+@SessionScoped
+public class DefaultClientWindowConfig implements ClientWindowConfig, Serializable
+{
+ /**
+ * We will set a cookie with this very name if a noscript link got clicked by the user
+ */
+ public static final String COOKIE_NAME_NOSCRIPT_ENABLED = "deltaspikeNoScriptEnabled";
+
+ /**
+ * The location of the default windowhandler resource
+ */
+ private static final String DEFAULT_WINDOW_HANDLER_HTML_FILE = "static/windowhandler.html";
+
+
+ private volatile Boolean javaScriptEnabled = null;
+
+ /**
+ * lazily initiated via {@link #getUserAgent(javax.faces.context.FacesContext)}
+ */
+ private volatile String userAgent = null;
+
+ /**
+ * Contains the cached ClientWindow handler html for this session.
+ */
+ private String clientWindowtml;
+
+ @Inject
+ private ProjectStage projectStage;
+
+
+ @Override
+ public boolean isJavaScriptEnabled()
+ {
+ if (javaScriptEnabled == null)
+ {
+ synchronized (this)
+ {
+ // double lock checking idiom on volatile variable works since java5
+ if (javaScriptEnabled == null)
+ {
+ // no info means that it is default -> true
+ javaScriptEnabled = Boolean.TRUE;
+
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ if (facesContext != null)
+ {
+ Cookie cookie = (Cookie) facesContext.getExternalContext().
+ getRequestCookieMap().get(COOKIE_NAME_NOSCRIPT_ENABLED);
+ if (cookie != null)
+ {
+ javaScriptEnabled = Boolean.parseBoolean(cookie.getValue());
+ }
+ }
+ }
+ }
+ }
+ return javaScriptEnabled;
+ }
+
+
+ @Override
+ public void setJavaScriptEnabled(boolean javaScriptEnabled)
+ {
+ this.javaScriptEnabled = Boolean.valueOf(javaScriptEnabled);
+ }
+
+ /**
+ * By default we use {@link ClientWindowRenderMode#CLIENTWINDOW} unless
+ * we detect a bot.
+ * Override this method to exclude other requests from getting accessed.
+ *
+ * @param facesContext
+ * @return
+ */
+ @Override
+ public ClientWindowRenderMode getClientWindowRenderMode(FacesContext facesContext)
+ {
+ if (!isJavaScriptEnabled())
+ {
+ return ClientWindowRenderMode.NONE;
+ }
+
+ String userAgent = getUserAgent(facesContext);
+
+ if (userAgent != null &&
+ ( userAgent.indexOf("bot") >= 0 || // Googlebot, etc
+ userAgent.indexOf("Bot") >= 0 || // BingBot, etc
+ userAgent.indexOf("Slurp") >= 0 || // Yahoo Slurp
+ userAgent.indexOf("Crawler") >= 0 // various other Crawlers
+ ) )
+ {
+ return ClientWindowRenderMode.NONE;
+ }
+
+ return ClientWindowRenderMode.CLIENTWINDOW;
+ }
+
+ @Override
+ public String getClientWindowHtml()
+ {
+ if (projectStage != ProjectStage.Development && clientWindowtml != null)
+ {
+ // use cached windowHandlerHtml except in Development
+ return clientWindowtml;
+ }
+
+ InputStream is = ClassUtils.getClassLoader(null).getResourceAsStream(getClientWindowResourceLocation());
+ StringBuffer sb = new StringBuffer();
+ try
+ {
+ byte[] buf = new byte[16 * 1024];
+ int bytesRead;
+ while ((bytesRead = is.read(buf)) != -1)
+ {
+ String sbuf = new String(buf, 0, bytesRead);
+ sb.append(sbuf);
+ }
+ }
+ catch (IOException e)
+ {
+ ExceptionUtils.throwAsRuntimeException(e);
+ }
+ finally
+ {
+ try
+ {
+ is.close();
+ }
+ catch (IOException e)
+ {
+ // do nothing, all fine so far
+ }
+ }
+
+ clientWindowtml = sb.toString();
+
+ return clientWindowtml;
+ }
+
+ /**
+ * This information will get stored as it cannot
+ * change during the session anyway.
+ * @return the UserAgent of the request.
+ */
+ public String getUserAgent(FacesContext facesContext)
+ {
+ if (userAgent == null)
+ {
+ synchronized (this)
+ {
+ if (userAgent == null)
+ {
+ Map<String, String[]> requestHeaders =
+ facesContext.getExternalContext().getRequestHeaderValuesMap();
+
+ if (requestHeaders != null &&
+ requestHeaders.containsKey("User-Agent"))
+ {
+ String[] userAgents = requestHeaders.get("User-Agent");
+ userAgent = userAgents.length > 0 ? userAgents[0] : null;
+ }
+ }
+ }
+ }
+
+ return userAgent;
+ }
+
+
+ /**
+ * Overwrite this to define your own ClientWindow handler html location.
+ * This will get picked up as resource from the classpath.
+ */
+ public String getClientWindowResourceLocation()
+ {
+ return DEFAULT_WINDOW_HANDLER_HTML_FILE;
+ }
+}
[3/3] git commit: DELTASPIKE-289 migrate windowhandler from CODI
Posted by st...@apache.org.
DELTASPIKE-289 migrate windowhandler from CODI
Project: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/commit/45770596
Tree: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/tree/45770596
Diff: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/diff/45770596
Branch: refs/heads/master
Commit: 45770596d64be289e202339bbc02ebb22aed3758
Parents: 30661b0
Author: Mark Struberg <st...@apache.org>
Authored: Sun Apr 21 20:56:15 2013 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Sun Apr 21 20:56:15 2013 +0200
----------------------------------------------------------------------
.../core/impl/scope/window/WindowBeanHolder.java | 7 +-
deltaspike/modules/jsf/impl/pom.xml | 4 +
.../request/DeltaSpikeLifecycleWrapper.java | 30 ++-
.../jsf/impl/scope/window/DefaultClientWindow.java | 165 +++++++++++++-
.../META-INF/resources/js/windowhandler.js | 180 +++++++++++++++
.../src/main/resources/static/windowhandler.html | 170 ++++++++++++++
6 files changed, 545 insertions(+), 11 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/45770596/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowBeanHolder.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowBeanHolder.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowBeanHolder.java
index b4acc46..429fa20 100644
--- a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowBeanHolder.java
+++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/scope/window/WindowBeanHolder.java
@@ -41,7 +41,7 @@ public class WindowBeanHolder implements Serializable
* value: the {@link ContextualStorage} which holds all the
* {@link javax.enterprise.inject.spi.Bean}s.
*/
- private volatile Map<String, ContextualStorage> storageMap = new ConcurrentHashMap<String, ContextualStorage>();
+ private Map<String, ContextualStorage> storageMap = new ConcurrentHashMap<String, ContextualStorage>();
/**
* This method will return the ContextualStorage or create a new one
@@ -67,6 +67,11 @@ public class WindowBeanHolder implements Serializable
return contextualStorage;
}
+ public Map<String, ContextualStorage> getStorageMap()
+ {
+ return storageMap;
+ }
+
/**
*
* This method will replace the storageMap and with
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/45770596/deltaspike/modules/jsf/impl/pom.xml
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/pom.xml b/deltaspike/modules/jsf/impl/pom.xml
index c71899f..ee9947c 100644
--- a/deltaspike/modules/jsf/impl/pom.xml
+++ b/deltaspike/modules/jsf/impl/pom.xml
@@ -65,6 +65,10 @@
<version>1.0</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_3.0_spec</artifactId>
+ </dependency>
<!-- we use Arquillian Warp to test our JSF apps -->
<dependency>
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/45770596/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/listener/request/DeltaSpikeLifecycleWrapper.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/listener/request/DeltaSpikeLifecycleWrapper.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/listener/request/DeltaSpikeLifecycleWrapper.java
index 9d4808d..0f91b33 100644
--- a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/listener/request/DeltaSpikeLifecycleWrapper.java
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/listener/request/DeltaSpikeLifecycleWrapper.java
@@ -19,7 +19,9 @@
package org.apache.deltaspike.jsf.impl.listener.request;
import org.apache.deltaspike.core.api.provider.BeanProvider;
+import org.apache.deltaspike.core.spi.scope.window.WindowContext;
import org.apache.deltaspike.core.util.ClassDeactivationUtils;
+import org.apache.deltaspike.jsf.spi.scope.window.ClientWindow;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseListener;
@@ -31,6 +33,9 @@ class DeltaSpikeLifecycleWrapper extends Lifecycle
private BeforeAfterJsfRequestBroadcaster beforeAfterJsfRequestBroadcaster;
+ private ClientWindow clientWindow;
+ private WindowContext windowContext;
+
private volatile Boolean initialized;
DeltaSpikeLifecycleWrapper(Lifecycle wrapped)
@@ -53,13 +58,19 @@ class DeltaSpikeLifecycleWrapper extends Lifecycle
@Override
public void execute(FacesContext facesContext)
{
+ lazyInit();
+
//TODO broadcastApplicationStartupBroadcaster();
broadcastBeforeFacesRequestEvent(facesContext);
- //X TODO add ClientWindow handling
- this.wrapped.execute(facesContext);
-
+ // ClientWindow handling
+ String windowId = clientWindow.getWindowId(facesContext);
+ if (windowId != null)
+ {
+ windowContext.activateWindow(windowId);
+ }
+ this.wrapped.execute(facesContext);
}
@Override
@@ -85,7 +96,6 @@ class DeltaSpikeLifecycleWrapper extends Lifecycle
private void broadcastBeforeFacesRequestEvent(FacesContext facesContext)
{
- lazyInit();
if (this.beforeAfterJsfRequestBroadcaster != null)
{
this.beforeAfterJsfRequestBroadcaster.broadcastBeforeJsfRequestEvent(facesContext);
@@ -103,15 +113,21 @@ class DeltaSpikeLifecycleWrapper extends Lifecycle
private synchronized void init()
{
// switch into paranoia mode
- if (this.initialized == null)
+ if (initialized == null)
{
if (ClassDeactivationUtils.isActivated(BeforeAfterJsfRequestBroadcaster.class))
{
- this.beforeAfterJsfRequestBroadcaster =
+ beforeAfterJsfRequestBroadcaster =
BeanProvider.getContextualReference(BeforeAfterJsfRequestBroadcaster.class, true);
+
+ clientWindow =
+ BeanProvider.getContextualReference(ClientWindow.class, true);
+
+ windowContext =
+ BeanProvider.getContextualReference(WindowContext.class, true);
}
- this.initialized = true;
+ initialized = true;
}
}
}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/45770596/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/DefaultClientWindow.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/DefaultClientWindow.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/DefaultClientWindow.java
index 4ab6d37..3ea9478 100644
--- a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/DefaultClientWindow.java
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/DefaultClientWindow.java
@@ -19,13 +19,20 @@
package org.apache.deltaspike.jsf.impl.scope.window;
import javax.enterprise.context.ApplicationScoped;
+import javax.faces.FacesException;
import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
import java.util.logging.Logger;
import org.apache.deltaspike.core.spi.scope.window.WindowContext;
+import org.apache.deltaspike.jsf.impl.util.JsfUtils;
import org.apache.deltaspike.jsf.spi.scope.window.ClientWindow;
import org.apache.deltaspike.jsf.spi.scope.window.ClientWindowConfig;
@@ -46,6 +53,24 @@ public class DefaultClientWindow implements ClientWindow
{
private static final Logger logger = Logger.getLogger(DefaultClientWindow.class.getName());
+ private static final String WINDOW_ID_COOKIE_PREFIX = "dsWindowId-";
+ private static final String DELTASPIKE_REQUEST_TOKEN = "dsRid";
+
+ private static final String UNINITIALIZED_WINDOW_ID_VALUE = "uninitializedWindowId";
+ private static final String WINDOW_ID_REPLACE_PATTERN = "$$windowIdValue$$";
+ private static final String NOSCRIPT_URL_REPLACE_PATTERN = "$$noscriptUrl$$";
+
+ /**
+ * Use this parameter to force a 'direct' request from the clients without any windowId detection
+ * We keep this name for backward compat with CODI.
+ */
+ private static final String NOSCRIPT_PARAMETER = "mfDirect";
+
+ /**
+ * This windowId will be used for all requests with disabled windowId feature
+ */
+ private static final String DEFAULT_WINDOW_ID = "default";
+
@Inject
private ClientWindowConfig clientWindowConfig;
@@ -59,16 +84,36 @@ public class DefaultClientWindow implements ClientWindow
{
if (ClientWindowRenderMode.NONE.equals(clientWindowConfig.getClientWindowRenderMode(facesContext)))
{
- return null;
+ // if this request should not get any window detection then we are done
+ return DEFAULT_WINDOW_ID;
}
- String windowId = null;
-
if (facesContext.isPostback())
{
+ // for POST we read the windowId from the WindowIdHolderComponent in our ViewRoot
return getPostBackWindowId(facesContext);
}
+ ExternalContext externalContext = facesContext.getExternalContext();
+
+ // and now for the GET request stuff
+ if (isNoscriptRequest(externalContext))
+ {
+ // the client has JavaScript disabled
+ clientWindowConfig.setJavaScriptEnabled(false);
+
+ return DEFAULT_WINDOW_ID;
+ }
+
+ String windowId = getVerifiedWindowIdFromCookie(externalContext);
+ if (windowId == null)
+ {
+ // GET request without windowId - send windowhandlerfilter.html to get the windowId
+ sendWindowHandlerHtml(externalContext, null);
+ facesContext.responseComplete();
+ }
+
+ // we have a valid windowId - set it and continue with the request
return windowId;
}
@@ -93,4 +138,118 @@ public class DefaultClientWindow implements ClientWindow
}
+ private boolean isNoscriptRequest(ExternalContext externalContext)
+ {
+ String noscript = externalContext.getRequestParameterMap().get(NOSCRIPT_PARAMETER);
+
+ return (noscript != null && "true".equals(noscript));
+ }
+
+
+ private void sendWindowHandlerHtml(ExternalContext externalContext, String windowId)
+ {
+ HttpServletResponse httpResponse = (HttpServletResponse) externalContext.getResponse();
+
+ try
+ {
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ httpResponse.setContentType("text/html");
+
+ String windowHandlerHtml = clientWindowConfig.getClientWindowHtml();
+
+ if (windowId == null)
+ {
+ windowId = UNINITIALIZED_WINDOW_ID_VALUE;
+ }
+
+ // set the windowId value in the javascript code
+ windowHandlerHtml = windowHandlerHtml.replace(WINDOW_ID_REPLACE_PATTERN, windowId);
+
+ // set the noscript-URL for users with no JavaScript
+ windowHandlerHtml =
+ windowHandlerHtml.replace(NOSCRIPT_URL_REPLACE_PATTERN, getNoscriptUrl(externalContext));
+
+ OutputStream os = httpResponse.getOutputStream();
+ try
+ {
+ os.write(windowHandlerHtml.getBytes());
+ }
+ finally
+ {
+ os.close();
+ }
+ }
+ catch (IOException ioe)
+ {
+ throw new FacesException(ioe);
+ }
+ }
+
+ private String getNoscriptUrl(ExternalContext externalContext)
+ {
+ String url = externalContext.getRequestPathInfo();
+ if (url == null)
+ {
+ url = "";
+ }
+
+ // only use the very last part of the url
+ int lastSlash = url.lastIndexOf('/');
+ if (lastSlash != -1)
+ {
+ url = url.substring(lastSlash + 1);
+ }
+
+ // add request parameter
+ url = JsfUtils.addPageParameters(externalContext, url, true);
+
+ // add noscript parameter
+ if (url.contains("?"))
+ {
+ url = url + "&";
+ }
+ else
+ {
+ url = url + "?";
+ }
+ url = url + NOSCRIPT_PARAMETER + "=true";
+
+ // NOTE that the url could contain data for an XSS attack
+ // like e.g. ?"></a><a href%3D"http://hacker.org/attack.html?a
+ // DO NOT REMOVE THE FOLLOWING LINES!
+ url = url.replace("\"", "");
+ url = url.replace("\'", "");
+
+ return url;
+ }
+
+ private String getVerifiedWindowIdFromCookie(ExternalContext externalContext)
+ {
+ String cookieName = WINDOW_ID_COOKIE_PREFIX + getRequestToken(externalContext);
+ Cookie cookie = (Cookie) externalContext.getRequestCookieMap().get(cookieName);
+
+ if (cookie != null)
+ {
+ // manually blast the cookie away, otherwise it pollutes the
+ // cookie storage in some browsers. E.g. Firefox doesn't
+ // cleanup properly, even if the max-age is reached.
+ cookie.setMaxAge(0);
+
+ return cookie.getValue();
+ }
+
+ return null;
+ }
+
+ private String getRequestToken(ExternalContext externalContext)
+ {
+ String requestToken = externalContext.getRequestParameterMap().get(DELTASPIKE_REQUEST_TOKEN);
+ if (requestToken != null)
+ {
+ return requestToken;
+ }
+
+ return "";
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/45770596/deltaspike/modules/jsf/impl/src/main/resources/META-INF/resources/js/windowhandler.js
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/resources/META-INF/resources/js/windowhandler.js b/deltaspike/modules/jsf/impl/src/main/resources/META-INF/resources/js/windowhandler.js
new file mode 100644
index 0000000..b563b31
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/resources/META-INF/resources/js/windowhandler.js
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+(function() {
+//wrapping so that all internal functions are privately scoped
+function isHtml5() {
+ try {
+ return !!localStorage.getItem;
+ } catch(e) {
+ return false;
+ }
+}
+
+// some browsers don't understand JSON - guess which one ... :(
+function stringify(someArray) {
+ if (JSON) {
+ return JSON.stringify(someArray);
+ }
+ return someArray.join("|||");
+}
+
+// store the current body in the html5 localstorage
+function storeWindowTree() {
+ // first we store all CSS we also need on the intermediate page
+ var headNodes = document.getElementsByTagName("head")[0].childNodes;
+ var oldSS = new Array();
+ var j = 0;
+ for (var i = 0; i < headNodes.length; i++) {
+ var tagName = headNodes[i].tagName;
+ if (tagName && equalsIgnoreCase(tagName, "link") &&
+ equalsIgnoreCase(headNodes[i].getAttribute("type"), "text/css")) {
+
+ // sort out media="print" and stuff
+ var media = headNodes[i].getAttribute("media");
+ if (!media || equalsIgnoreCase(media, "all") || equalsIgnoreCase(media, 'screen')) {
+ oldSS[j++] = headNodes[i].getAttribute("href");
+ }
+ }
+ }
+ localStorage.setItem(window.name + '_css', stringify(oldSS));
+ var body = document.getElementsByTagName("body")[0];
+ localStorage.setItem(window.name + '_body', body.innerHTML);
+ //X TODO: store ALL attributes of the body tag
+ localStorage.setItem(window.name + '_bodyAttrs', body.getAttribute("class"));
+ return true;
+}
+
+function equalsIgnoreCase(source, destination) {
+ //either both are not set or null
+ if (!source && !destination) {
+ return true;
+ }
+ //source or dest is set while the other is not
+ if (!source || !destination) return false;
+
+ //in any other case we do a strong string comparison
+ return source.toLowerCase() === destination.toLowerCase();
+}
+
+/** This method will be called onWindowLoad and after AJAX success */
+function applyOnClick() {
+ var links = document.getElementsByTagName("a");
+ for (var i = 0; i < links.length; i++) {
+ if (!links[i].onclick) {
+ links[i].onclick = function() {storeWindowTree(); return true;};
+ } else {
+ // prevent double decoration
+ if (!("" + links[i].onclick).match(".*storeWindowTree().*")) {
+ //the function wrapper is important otherwise the
+ //last onclick handler would be assigned to oldonclick
+ (function storeEvent() {
+ var oldonclick = links[i].onclick;
+ links[i].onclick = function(evt) {
+ //ie handling added
+ evt = evt || window.event;
+
+ return storeWindowTree() && oldonclick(evt);
+ };
+ })();
+ }
+ }
+ }
+}
+
+function getUrlParameter(name) {
+ var url = window.location.href;
+ var vars = url.split(/&|\?/g);
+ for (var i=0; vars != null && i < vars.length; i++) {
+ var pair = vars[i].split("=");
+ if (pair[0]==name) {
+ return pair[1];
+ }
+ }
+ return null;
+}
+function setUrlParam(baseUrl, paramName, paramValue) {
+ var query = baseUrl;
+ var vars = query.split(/&|\?/g);
+ var newQuery = "";
+ var iParam = 0;
+ var paramFound = false;
+ for (var i=0; vars != null && i < vars.length; i++) {
+ var pair = vars[i].split("=");
+ if (pair.length == 1) {
+ newQuery = pair[0];
+ } else {
+ if (pair[0] != paramName) {
+ var amp = iParam++ > 0 ? "&" : "?";
+ newQuery = newQuery + amp + pair[0] + "=" + pair[1];
+ } else {
+ paramFound = true;
+ if (paramValue) {
+ var amp = iParam++ > 0 ? "&" : "?";
+ newQuery = newQuery + amp + paramName + "=" + paramValue;
+ }
+ }
+ }
+ }
+ if (!paramFound && paramValue) {
+ var amp = iParam++ > 0 ? "&" : "?";
+ newQuery = newQuery + amp + paramName + "=" + paramValue;
+ }
+ return newQuery;
+}
+// this method runs to ensure that windowIds get checked even if no windowhandler.html is used
+function assertWindowId() {
+ if (!window.name || window.name.length < 1) {
+ url = setUrlParam(window.location.href, 'windowId', null);
+ window.name = 'tempWindowId';
+ window.location = url;
+ }
+}
+
+function eraseRequestCookie() {
+ var requestToken = getUrlParameter('dsRid'); // random request param
+ if (requestToken) {
+ var cookieName = 'dsiWindowId-' + requestToken;
+ var date = new Date();
+ date.setTime(date.getTime()-(10*24*60*60*1000)); // - 10 day
+ var expires = "; expires="+date.toGMTString();
+ document.cookie = cookieName+"="+expires+"; path=/";
+ }
+}
+
+var ajaxOnClick = function ajaxDecorateClick(event) {
+ if (event.status=="success") {
+ applyOnClick();
+ }
+}
+
+var oldWindowOnLoad = window.onload;
+
+window.onload = function(evt) {
+ try {
+ (oldWindowOnLoad)? oldWindowOnLoad(evt): null;
+ } finally {
+ eraseRequestCookie(); // manually erase the old dsRid cookie because Firefox doesn't do it properly
+ assertWindowId();
+ if (isHtml5()) {
+ applyOnClick();
+ jsf.ajax.addOnEvent(ajaxOnClick);
+ }
+ }
+}
+})();
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/45770596/deltaspike/modules/jsf/impl/src/main/resources/static/windowhandler.html
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/resources/static/windowhandler.html b/deltaspike/modules/jsf/impl/src/main/resources/static/windowhandler.html
new file mode 100644
index 0000000..c096722
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/resources/static/windowhandler.html
@@ -0,0 +1,170 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+ 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.
+-->
+<html>
+
+<head><title>Loading...</title></head>
+<body $$bodyAttributes$$><div id="message" style="position:absolute;left:40%;top:40%">
+ Your browser does not support JavaScript.
+ Click <a href="$$noscriptUrl$$">here</a> to continue without JavaScript.
+</div></body>
+
+<script type="text/javascript" >
+function isHtml5() {
+ try { return !!localStorage.getItem;
+ } catch(e) { return false; }
+}
+function unstringify(serialized) {
+ if (JSON) { return JSON.parse(serialized); }
+ return serialized.split("|||");
+}
+function getOldBody() {
+ if (window.name.length != null) {
+ return localStorage.getItem(window.name + '_body');
+ }
+}
+function getOldBodyAttrs() {
+ if (window.name.length != null) {
+ return localStorage.getItem(window.name + '_bodyAttrs');
+ }
+}
+function getOldCss() {
+ if (window.name.length != null) {
+ return unstringify(localStorage.getItem(window.name + '_css'));
+ }
+}
+
+function addCss(url) {
+ var newSS = document.createElement("style");
+ newSS.setAttribute("rel", "stylesheet");
+ newSS.setAttribute("type", "text/css");
+ newSS.appendChild(document.createTextNode("@import url(" +url + ");"));
+ document.getElementsByTagName("head")[0].appendChild(newSS);
+}
+
+function loadCss(clean) {
+ if (!isHtml5()) { // only do this stuff on html browsers
+ return;
+ }
+ var oldCss = getOldCss();
+ if (window.name && oldCss) {
+ for (i=0; oldCss && i< oldCss.length; i++) {
+ addCss(oldCss[i]);
+ }
+ if (clean) {
+ localStorage.removeItem(window.name + '_css');
+ }
+ }
+}
+
+function replaceContent() {
+ if (!isHtml5()) { // only do this stuff on html browsers
+ document.getElementById('message').textContent = "Loading...";
+ return;
+ }
+ loadCss(false);
+
+ var oldBody = getOldBody();
+
+ if (window.name && oldBody) {
+ document.body.innerHTML = oldBody;
+
+ //X TODO should restore all attribs of the body tag
+ document.body.setAttribute("class", getOldBodyAttrs());
+ document.body.setAttribute("style", " cursor: wait !important;");
+
+ localStorage.removeItem(window.name + '_body');
+ localStorage.removeItem(window.name + '_bodyAttrs');
+
+ // overlay the doc with an un-clickable full-size div
+ var newDiv = document.createElement("div");
+ newDiv.setAttribute("style", "position:absolute; z-index:1000; background-color:transparent; top:0; left:0; width:100%; height: 100%");
+ newDiv.setAttribute("class", "fulldiv");
+ document.body.appendChild(newDiv);
+ } else {
+ document.getElementById('message').textContent = "Loading...";
+ }
+}
+
+function setUrlParam(baseUrl, paramName, paramValue) {
+ var query = baseUrl;
+ var vars = query.split(/&|\?/g);
+ var newQuery = "";
+ var iParam = 0;
+ var paramFound = false;
+ for (var i=0; vars != null && i < vars.length; i++) {
+ var pair = vars[i].split("=");
+ if (pair.length == 1) {
+ newQuery = pair[0];
+ } else {
+ if (pair[0] != paramName) {
+ var amp = iParam++ > 0 ? "&" : "?";
+ newQuery = newQuery + amp + pair[0] + "=" + pair[1];
+ } else {
+ paramFound = true;
+ var amp = iParam++ > 0 ? "&" : "?";
+ newQuery = newQuery + amp + paramName + "=" + paramValue;
+ }
+ }
+ }
+ if (!paramFound) {
+ var amp = iParam++ > 0 ? "&" : "?";
+ newQuery = newQuery + amp + paramName + "=" + paramValue;
+ }
+ return newQuery;
+}
+
+replaceContent();
+
+window.onload = function() {
+ loadCss(true);
+ // this will be replaced in the phase listener
+ var windowId = '$$windowIdValue$$';
+ if (windowId == 'uninitializedWindowId') {
+ windowId = window.name
+ }
+ if (!windowId || windowId.length < 1) {
+ // request a new windowId
+ windowId = 'automatedEntryPoint';
+ }
+
+ window.name = windowId;
+
+ // uncomment the following line to debug the intermediate page
+ // if (!confirm('reload?')) { return true; }
+
+ // 3 seconds expiry time
+ var expdt = new Date();
+ expdt.setTime(expdt.getTime()+(3*1000));
+ var expires = "; expires="+expdt.toGMTString();
+
+ var requestToken = Math.floor(Math.random()*1001);
+ var newUrl = setUrlParam(window.location.href, "dsRid", requestToken);
+ newUrl = setUrlParam(newUrl, "windowId", windowId);
+
+ document.cookie = 'dsWindowId-' + requestToken + '=' + windowId + expires+"; path=/";
+
+ window.location = newUrl;
+}
+</script>
+
+</html>
+
[2/3] git commit: DELTASPIKE-289 add postback handling for windowId
Posted by st...@apache.org.
DELTASPIKE-289 add postback handling for windowId
Project: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/commit/30661b05
Tree: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/tree/30661b05
Diff: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/diff/30661b05
Branch: refs/heads/master
Commit: 30661b0551940cf7e40b3db2dd3263b3d8b23c2a
Parents: ff5df7d
Author: Mark Struberg <st...@apache.org>
Authored: Sun Apr 21 19:48:47 2013 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Sun Apr 21 19:48:47 2013 +0200
----------------------------------------------------------------------
.../META-INF/apache-deltaspike.properties | 26 +-
.../jsf/spi/scope/window/ClientWindow.java | 51 ++++
.../jsf/spi/scope/window/ClientWindowConfig.java | 84 ++++++
.../scope/window/DefaultClientWindowConfig.java | 218 +++++++++++++++
.../jsf/spi/window/ClientWindowConfig.java | 79 ------
.../jsf/spi/window/DefaultClientWindowConfig.java | 215 --------------
.../request/DeltaSpikeLifecycleWrapper.java | 3 +
.../jsf/impl/scope/window/DefaultClientWindow.java | 96 +++++++
.../impl/scope/window/WindowIdHolderComponent.java | 154 ++++++++++
.../scope/window/WindowIdRenderKitFactory.java | 101 +++++++
.../scope/window/WindowIdRenderKitWrapper.java | 92 ++++++
.../src/main/resources/META-INF/faces-config.xml | 3 +-
12 files changed, 814 insertions(+), 308 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/core/impl/src/test/resources/META-INF/apache-deltaspike.properties
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/test/resources/META-INF/apache-deltaspike.properties b/deltaspike/core/impl/src/test/resources/META-INF/apache-deltaspike.properties
index c20b420..b935ffc 100644
--- a/deltaspike/core/impl/src/test/resources/META-INF/apache-deltaspike.properties
+++ b/deltaspike/core/impl/src/test/resources/META-INF/apache-deltaspike.properties
@@ -1,19 +1,19 @@
-#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
+# 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.
+# 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.
testProperty03=test_value_03
org.apache.deltaspike.core.spi.activation.ClassDeactivator=org.apache.deltaspike.test.core.impl.activation.TestClassDeactivator
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/ClientWindow.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/ClientWindow.java b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/ClientWindow.java
new file mode 100644
index 0000000..d272f33
--- /dev/null
+++ b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/ClientWindow.java
@@ -0,0 +1,51 @@
+/*
+ * 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.deltaspike.jsf.spi.scope.window;
+
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>API to interact with the window/browser tab handling.
+ * This originally got implemented in Apache MyFaces CODI
+ * which was the basis for the respective feature in JSF-2.2.
+ * We now orientate us a bit on the JSF-2.2 API for making it
+ * easier to provide this feature for JSF-2.0, JSF-2.1 and also
+ * JSF-2.2 JSF implementations.</p>
+ *
+ * <p>Please not that in JSF-2.2 a <code>javax.faces.lifecycle.ClientWindow</code>
+ * instance gets created for each and every request, but in DeltaSpike our
+ * ClientWindow instances are most likely @ApplicationScoped.
+ * </p>
+ */
+public interface ClientWindow
+{
+
+ /**
+ * Extract the windowId for the current request.
+ * This method is intended to get executed at the start of the JSF lifecycle.
+ * We also need to take care about JSF-2.2 ClientWindow in the future.
+ * Depending on the {@link ClientWindowConfig.ClientWindowRenderMode} and
+ * after consulting {@link ClientWindowConfig} we will first send an
+ * intermediate page if the request is an initial GET request.
+ *
+ * @param facesContext for the request
+ * @return the extracted WindowId of the Request, or <code>null</code> if there is no window assigned.
+ */
+ String getWindowId(FacesContext facesContext);
+}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/ClientWindowConfig.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/ClientWindowConfig.java b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/ClientWindowConfig.java
new file mode 100644
index 0000000..dd3c5db
--- /dev/null
+++ b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/ClientWindowConfig.java
@@ -0,0 +1,84 @@
+/*
+ * 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.deltaspike.jsf.spi.scope.window;
+
+import javax.faces.context.FacesContext;
+
+/**
+ * Configuration for ClientWindow handler which is used
+ * to determine the correct windowId for ?WindowScoped beans.
+ */
+public interface ClientWindowConfig
+{
+ public enum ClientWindowRenderMode
+ {
+ /**
+ * Any window or browser tab detection is disabled for this request
+ */
+ NONE,
+
+ /**
+ * <p>The GET request results in an intermediate small html page which
+ * checks if the browser tab fits the selected windowId</p>
+ * <p>The ClientWindow html extracts the windowId from the window.name and
+ * enforces a 2nd GET which will contain the windowId and will get routed
+ * through to the target JSF page.</p>
+ */
+ CLIENTWINDOW,
+
+ /**
+ * Render each GET request with the windowId you get during the request
+ * and perform a lazy check on the client side via JavaScript or similar.
+ */
+ LAZY,
+
+ /**
+ * If you set this mode, you also need to provide an own {@link ClientWindow} implementation.
+ */
+ CUSTOM
+
+ }
+
+ /**
+ * @return whether JavaScript is enabled
+ */
+ boolean isJavaScriptEnabled();
+
+ /**
+ * @param javaScriptEnabled whether JavaScript is enabled
+ */
+ void setJavaScriptEnabled(boolean javaScriptEnabled);
+
+ /**
+ * Determine whether this request should take care of clientWindow detection.
+ * This can e.g. get disabled for download pages or if a useragent doesn't
+ * support html5 or any other required technique.
+ * This only gets checked for GET requests!
+ *
+ * @param facesContext
+ * @return the selected ClientWindowRenderMode
+ */
+ ClientWindowRenderMode getClientWindowRenderMode(FacesContext facesContext);
+
+ /**
+ * @return the prepared html which gets sent out to the client as intermediate client window.
+ */
+ String getClientWindowHtml();
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/DefaultClientWindowConfig.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/DefaultClientWindowConfig.java b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/DefaultClientWindowConfig.java
new file mode 100644
index 0000000..a3bcf22
--- /dev/null
+++ b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/scope/window/DefaultClientWindowConfig.java
@@ -0,0 +1,218 @@
+/*
+ * 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.deltaspike.jsf.spi.scope.window;
+
+import javax.enterprise.context.SessionScoped;
+import javax.faces.context.FacesContext;
+import javax.inject.Inject;
+import javax.servlet.http.Cookie;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.Map;
+
+import org.apache.deltaspike.core.api.projectstage.ProjectStage;
+import org.apache.deltaspike.core.util.ClassUtils;
+import org.apache.deltaspike.core.util.ExceptionUtils;
+
+/**
+ * <p>Default implementation of {@link ClientWindowConfig}.
+ * By default it will use the internal <code>windowhandler.html</code></p>
+ *
+ * <p>You can @Specializes this class to tweak the configuration or
+ * provide a completely new implementation as @Alternative.</p>
+ */
+@SessionScoped
+public class DefaultClientWindowConfig implements ClientWindowConfig, Serializable
+{
+ /**
+ * We will set a cookie with this very name if a noscript link got clicked by the user
+ */
+ public static final String COOKIE_NAME_NOSCRIPT_ENABLED = "deltaspikeNoScriptEnabled";
+
+ /**
+ * The location of the default windowhandler resource
+ */
+ private static final String DEFAULT_WINDOW_HANDLER_HTML_FILE = "static/windowhandler.html";
+
+
+ private volatile Boolean javaScriptEnabled = null;
+
+ /**
+ * lazily initiated via {@link #getUserAgent(javax.faces.context.FacesContext)}
+ */
+ private volatile String userAgent = null;
+
+ /**
+ * Contains the cached ClientWindow handler html for this session.
+ */
+ private String clientWindowtml;
+
+ @Inject
+ private ProjectStage projectStage;
+
+
+ @Override
+ public boolean isJavaScriptEnabled()
+ {
+ if (javaScriptEnabled == null)
+ {
+ synchronized (this)
+ {
+ // double lock checking idiom on volatile variable works since java5
+ if (javaScriptEnabled == null)
+ {
+ // no info means that it is default -> true
+ javaScriptEnabled = Boolean.TRUE;
+
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ if (facesContext != null)
+ {
+ Cookie cookie = (Cookie) facesContext.getExternalContext().
+ getRequestCookieMap().get(COOKIE_NAME_NOSCRIPT_ENABLED);
+ if (cookie != null)
+ {
+ javaScriptEnabled = Boolean.parseBoolean(cookie.getValue());
+ }
+ }
+ }
+ }
+ }
+ return javaScriptEnabled;
+ }
+
+
+ @Override
+ public void setJavaScriptEnabled(boolean javaScriptEnabled)
+ {
+ this.javaScriptEnabled = Boolean.valueOf(javaScriptEnabled);
+ }
+
+ /**
+ * By default we use {@link ClientWindowRenderMode#CLIENTWINDOW} unless
+ * we detect a bot.
+ * Override this method to exclude other requests from getting accessed.
+ *
+ * @param facesContext
+ * @return
+ */
+ @Override
+ public ClientWindowRenderMode getClientWindowRenderMode(FacesContext facesContext)
+ {
+ if (!isJavaScriptEnabled())
+ {
+ return ClientWindowRenderMode.NONE;
+ }
+
+ String userAgent = getUserAgent(facesContext);
+
+ if (userAgent != null &&
+ ( userAgent.indexOf("bot") >= 0 || // Googlebot, etc
+ userAgent.indexOf("Bot") >= 0 || // BingBot, etc
+ userAgent.indexOf("Slurp") >= 0 || // Yahoo Slurp
+ userAgent.indexOf("Crawler") >= 0 // various other Crawlers
+ ) )
+ {
+ return ClientWindowRenderMode.NONE;
+ }
+
+ return ClientWindowRenderMode.CLIENTWINDOW;
+ }
+
+ @Override
+ public String getClientWindowHtml()
+ {
+ if (projectStage != ProjectStage.Development && clientWindowtml != null)
+ {
+ // use cached windowHandlerHtml except in Development
+ return clientWindowtml;
+ }
+
+ InputStream is = ClassUtils.getClassLoader(null).getResourceAsStream(getClientWindowResourceLocation());
+ StringBuffer sb = new StringBuffer();
+ try
+ {
+ byte[] buf = new byte[16 * 1024];
+ int bytesRead;
+ while ((bytesRead = is.read(buf)) != -1)
+ {
+ String sbuf = new String(buf, 0, bytesRead);
+ sb.append(sbuf);
+ }
+ }
+ catch (IOException e)
+ {
+ ExceptionUtils.throwAsRuntimeException(e);
+ }
+ finally
+ {
+ try
+ {
+ is.close();
+ }
+ catch (IOException e)
+ {
+ // do nothing, all fine so far
+ }
+ }
+
+ clientWindowtml = sb.toString();
+
+ return clientWindowtml;
+ }
+
+ /**
+ * This information will get stored as it cannot
+ * change during the session anyway.
+ * @return the UserAgent of the request.
+ */
+ public String getUserAgent(FacesContext facesContext)
+ {
+ if (userAgent == null)
+ {
+ synchronized (this)
+ {
+ if (userAgent == null)
+ {
+ Map<String, String[]> requestHeaders =
+ facesContext.getExternalContext().getRequestHeaderValuesMap();
+
+ if (requestHeaders != null &&
+ requestHeaders.containsKey("User-Agent"))
+ {
+ String[] userAgents = requestHeaders.get("User-Agent");
+ userAgent = userAgents.length > 0 ? userAgents[0] : null;
+ }
+ }
+ }
+ }
+
+ return userAgent;
+ }
+
+
+ /**
+ * Overwrite this to define your own ClientWindow handler html location.
+ * This will get picked up as resource from the classpath.
+ */
+ public String getClientWindowResourceLocation()
+ {
+ return DEFAULT_WINDOW_HANDLER_HTML_FILE;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/ClientWindowConfig.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/ClientWindowConfig.java b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/ClientWindowConfig.java
deleted file mode 100644
index 94690ec..0000000
--- a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/ClientWindowConfig.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.deltaspike.jsf.spi.window;
-
-import javax.faces.context.FacesContext;
-
-/**
- * Configuration for ClientWindow handler which is used
- * to determine the correct windowId for ?WindowScoped beans.
- */
-public interface ClientWindowConfig
-{
- public enum ClientWindowRenderMode
- {
- /**
- * Any window or browser tab detection is disabled for this request
- */
- NONE,
-
- /**
- * <p>The GET request results in an intermediate small html page which
- * checks if the browser tab fits the selected windowId</p>
- * <p>The ClientWindow html extracts the windowId from the window.name and
- * enforces a 2nd GET which will contain the windowId and will get routed
- * through to the target JSF page.</p>
- */
- CLIENTWINDOW,
-
- /**
- * Render each GET request with the windowId you get during the request
- * and perform a lazy check on the client side via JavaScript or similar.
- */
- LAZY
-
- }
-
- /**
- * @return whether JavaScript is enabled
- */
- boolean isJavaScriptEnabled();
-
- /**
- * @param javaScriptEnabled whether JavaScript is enabled
- */
- void setJavaScriptEnabled(boolean javaScriptEnabled);
-
- /**
- * Determine whether this request should take care of clientWindow detection.
- * This can e.g. get disabled for download pages or if a useragent doesn't
- * support html5 or any other required technique.
- * This only gets checked for GET requests!
- *
- * @param facesContext
- * @return the selected ClientWindowRenderMode
- */
- ClientWindowRenderMode getClientWindowRenderMode(FacesContext facesContext);
-
- /**
- * @return the prepared html which gets sent out to the client as intermediate client window.
- */
- String getClientWindowHtml();
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/DefaultClientWindowConfig.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/DefaultClientWindowConfig.java b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/DefaultClientWindowConfig.java
deleted file mode 100644
index dbec591..0000000
--- a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/spi/window/DefaultClientWindowConfig.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * 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.deltaspike.jsf.spi.window;
-
-import javax.enterprise.context.SessionScoped;
-import javax.faces.context.FacesContext;
-import javax.inject.Inject;
-import javax.servlet.http.Cookie;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Serializable;
-import java.util.Map;
-
-import org.apache.deltaspike.core.api.projectstage.ProjectStage;
-import org.apache.deltaspike.core.util.ClassUtils;
-import org.apache.deltaspike.core.util.ExceptionUtils;
-
-/**
- * Default implementation of {@link ClientWindowConfig}.
- * It will use the internal <code>windowhandler.html</code>
- */
-@SessionScoped
-public class DefaultClientWindowConfig implements ClientWindowConfig, Serializable
-{
- /**
- * We will set a cookie with this very name if a noscript link got clicked by the user
- */
- public static final String COOKIE_NAME_NOSCRIPT_ENABLED = "deltaspikeNoScriptEnabled";
-
- /**
- * The location of the default windowhandler resource
- */
- private static final String DEFAULT_WINDOW_HANDLER_HTML_FILE = "static/windowhandler.html";
-
-
- private volatile Boolean javaScriptEnabled = null;
-
- /**
- * lazily initiated via {@link #getUserAgent(javax.faces.context.FacesContext)}
- */
- private volatile String userAgent = null;
-
- /**
- * Contains the cached ClientWindow handler html for this session.
- */
- private String clientWindowtml;
-
- @Inject
- private ProjectStage projectStage;
-
-
- @Override
- public boolean isJavaScriptEnabled()
- {
- if (javaScriptEnabled == null)
- {
- synchronized (this)
- {
- // double lock checking idiom on volatile variable works since java5
- if (javaScriptEnabled == null)
- {
- // no info means that it is default -> true
- javaScriptEnabled = Boolean.TRUE;
-
- FacesContext facesContext = FacesContext.getCurrentInstance();
- if (facesContext != null)
- {
- Cookie cookie = (Cookie) facesContext.getExternalContext().
- getRequestCookieMap().get(COOKIE_NAME_NOSCRIPT_ENABLED);
- if (cookie != null)
- {
- javaScriptEnabled = Boolean.parseBoolean(cookie.getValue());
- }
- }
- }
- }
- }
- return javaScriptEnabled;
- }
-
-
- @Override
- public void setJavaScriptEnabled(boolean javaScriptEnabled)
- {
- this.javaScriptEnabled = Boolean.valueOf(javaScriptEnabled);
- }
-
- /**
- * By default we use {@link ClientWindowRenderMode#CLIENTWINDOW} unless
- * we detect a bot.
- * Override this method to exclude other requests from getting accessed.
- *
- * @param facesContext
- * @return
- */
- @Override
- public ClientWindowRenderMode getClientWindowRenderMode(FacesContext facesContext)
- {
- if (!isJavaScriptEnabled())
- {
- return ClientWindowRenderMode.NONE;
- }
-
- String userAgent = getUserAgent(facesContext);
-
- if (userAgent != null &&
- ( userAgent.indexOf("bot") >= 0 || // Googlebot, etc
- userAgent.indexOf("Bot") >= 0 || // BingBot, etc
- userAgent.indexOf("Slurp") >= 0 || // Yahoo Slurp
- userAgent.indexOf("Crawler") >= 0 // various other Crawlers
- ) )
- {
- return ClientWindowRenderMode.NONE;
- }
-
- return ClientWindowRenderMode.CLIENTWINDOW;
- }
-
- @Override
- public String getClientWindowHtml()
- {
- if (projectStage != ProjectStage.Development && clientWindowtml != null)
- {
- // use cached windowHandlerHtml except in Development
- return clientWindowtml;
- }
-
- InputStream is = ClassUtils.getClassLoader(null).getResourceAsStream(getClientWindowResourceLocation());
- StringBuffer sb = new StringBuffer();
- try
- {
- byte[] buf = new byte[16 * 1024];
- int bytesRead;
- while ((bytesRead = is.read(buf)) != -1)
- {
- String sbuf = new String(buf, 0, bytesRead);
- sb.append(sbuf);
- }
- }
- catch (IOException e)
- {
- ExceptionUtils.throwAsRuntimeException(e);
- }
- finally
- {
- try
- {
- is.close();
- }
- catch (IOException e)
- {
- // do nothing, all fine so far
- }
- }
-
- clientWindowtml = sb.toString();
-
- return clientWindowtml;
- }
-
- /**
- * This information will get stored as it cannot
- * change during the session anyway.
- * @return the UserAgent of the request.
- */
- public String getUserAgent(FacesContext facesContext)
- {
- if (userAgent == null)
- {
- synchronized (this)
- {
- if (userAgent == null)
- {
- Map<String, String[]> requestHeaders =
- facesContext.getExternalContext().getRequestHeaderValuesMap();
-
- if (requestHeaders != null &&
- requestHeaders.containsKey("User-Agent"))
- {
- String[] userAgents = requestHeaders.get("User-Agent");
- userAgent = userAgents.length > 0 ? userAgents[0] : null;
- }
- }
- }
- }
-
- return userAgent;
- }
-
-
- /**
- * Overwrite this to define your own ClientWindow handler html location.
- * This will get picked up as resource from the classpath.
- */
- public String getClientWindowResourceLocation()
- {
- return DEFAULT_WINDOW_HANDLER_HTML_FILE;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/listener/request/DeltaSpikeLifecycleWrapper.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/listener/request/DeltaSpikeLifecycleWrapper.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/listener/request/DeltaSpikeLifecycleWrapper.java
index 28ad66d..9d4808d 100644
--- a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/listener/request/DeltaSpikeLifecycleWrapper.java
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/listener/request/DeltaSpikeLifecycleWrapper.java
@@ -56,7 +56,10 @@ class DeltaSpikeLifecycleWrapper extends Lifecycle
//TODO broadcastApplicationStartupBroadcaster();
broadcastBeforeFacesRequestEvent(facesContext);
+ //X TODO add ClientWindow handling
this.wrapped.execute(facesContext);
+
+
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/DefaultClientWindow.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/DefaultClientWindow.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/DefaultClientWindow.java
new file mode 100644
index 0000000..4ab6d37
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/DefaultClientWindow.java
@@ -0,0 +1,96 @@
+/*
+ * 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.deltaspike.jsf.impl.scope.window;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+import javax.inject.Inject;
+
+import java.util.logging.Logger;
+
+import org.apache.deltaspike.core.spi.scope.window.WindowContext;
+import org.apache.deltaspike.jsf.spi.scope.window.ClientWindow;
+import org.apache.deltaspike.jsf.spi.scope.window.ClientWindowConfig;
+
+import static org.apache.deltaspike.jsf.spi.scope.window.ClientWindowConfig.ClientWindowRenderMode;
+
+/**
+ * This is the default implementation of the window/browser tab
+ * detection handling for JSF applications.
+ * This is to big degrees a port of Apache MyFaces CODI
+ * ClientSideWindowHandler.
+ *
+ * It will act according to the configured {@link ClientWindowRenderMode}.
+ *
+ *
+ */
+@ApplicationScoped
+public class DefaultClientWindow implements ClientWindow
+{
+ private static final Logger logger = Logger.getLogger(DefaultClientWindow.class.getName());
+
+
+ @Inject
+ private ClientWindowConfig clientWindowConfig;
+
+ @Inject
+ private WindowContext windowContext;
+
+
+ @Override
+ public String getWindowId(FacesContext facesContext)
+ {
+ if (ClientWindowRenderMode.NONE.equals(clientWindowConfig.getClientWindowRenderMode(facesContext)))
+ {
+ return null;
+ }
+
+ String windowId = null;
+
+ if (facesContext.isPostback())
+ {
+ return getPostBackWindowId(facesContext);
+ }
+
+ return windowId;
+ }
+
+ /**
+ * Extract the windowId for http POST
+ */
+ private String getPostBackWindowId(FacesContext facesContext)
+ {
+ UIViewRoot uiViewRoot = facesContext.getViewRoot();
+
+ if (uiViewRoot != null)
+ {
+ WindowIdHolderComponent existingWindowIdHolder
+ = WindowIdHolderComponent.getWindowIdHolderComponent(uiViewRoot);
+ if (existingWindowIdHolder != null)
+ {
+ return existingWindowIdHolder.getWindowId();
+ }
+ }
+
+ return null;
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdHolderComponent.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdHolderComponent.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdHolderComponent.java
new file mode 100644
index 0000000..e4f7326
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdHolderComponent.java
@@ -0,0 +1,154 @@
+/*
+ * 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.deltaspike.jsf.impl.scope.window;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIOutput;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * UI Component holder for the windowId in case of post-backs.
+ * We store this component as direct child in the ViewRoot
+ * and evaluate it's value on postbacks.
+ */
+public class WindowIdHolderComponent extends UIOutput
+{
+ private static final Logger logger = Logger.getLogger(WindowIdHolderComponent.class.getName());
+
+ private String windowId;
+
+ /**
+ * Default constructor might be invoked by the jsf implementation
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public WindowIdHolderComponent()
+ {
+ }
+
+ /**
+ * Constructor which creates the holder for the given window-id
+ * @param windowId current window-id
+ */
+ public WindowIdHolderComponent(String windowId)
+ {
+ this.windowId = windowId;
+ }
+
+ /**
+ * Needed for server-side window-handler and client-side window handler for supporting postbacks
+ */
+ public static void addWindowIdHolderComponent(FacesContext facesContext, String windowId)
+ {
+ if (windowId == null || windowId.length() == 0)
+ {
+ return;
+ }
+
+ UIViewRoot uiViewRoot = facesContext.getViewRoot();
+
+ if (uiViewRoot == null)
+ {
+ return;
+ }
+
+ WindowIdHolderComponent existingWindowIdHolder = getWindowIdHolderComponent(uiViewRoot);
+ if (existingWindowIdHolder != null)
+ {
+ if (!windowId.equals(existingWindowIdHolder.getWindowId()))
+ {
+ logger.log(Level.FINE, "updating WindowIdHolderComponent from %1 to %2",
+ new Object[]{existingWindowIdHolder.getId(), windowId});
+
+ existingWindowIdHolder.changeWindowId(windowId);
+ }
+ return;
+ }
+ else
+ {
+ // add as first child
+ uiViewRoot.getChildren().add(0, new WindowIdHolderComponent(windowId));
+ }
+ }
+
+ public static WindowIdHolderComponent getWindowIdHolderComponent(UIViewRoot uiViewRoot)
+ {
+ List<UIComponent> uiComponents = uiViewRoot.getChildren();
+
+ // performance improvement - don't change - see EXTCDI-256 :
+ for (int i = 0, size = uiComponents.size(); i < size; i++)
+ {
+ UIComponent uiComponent = uiComponents.get(i);
+ if (uiComponent instanceof WindowIdHolderComponent)
+ {
+ //in this case we have the same view-root
+ return (WindowIdHolderComponent) uiComponent;
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object saveState(FacesContext facesContext)
+ {
+ Object[] values = new Object[2];
+ values[0] = super.saveState(facesContext);
+ values[1] = windowId;
+ return values;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void restoreState(FacesContext facesContext, Object state)
+ {
+ if (state == null)
+ {
+ return;
+ }
+
+ Object[] values = (Object[]) state;
+ super.restoreState(facesContext, values[0]);
+
+ windowId = (String) values[1];
+ }
+
+ /**
+ * @return the current windowId
+ */
+ public String getWindowId()
+ {
+ return windowId;
+ }
+
+ void changeWindowId(String windowId)
+ {
+ this.windowId = windowId;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdRenderKitFactory.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdRenderKitFactory.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdRenderKitFactory.java
new file mode 100644
index 0000000..883ddb0
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdRenderKitFactory.java
@@ -0,0 +1,101 @@
+/*
+ * 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.deltaspike.jsf.impl.scope.window;
+
+import javax.faces.context.FacesContext;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+import java.util.Iterator;
+
+import org.apache.deltaspike.core.spi.activation.Deactivatable;
+import org.apache.deltaspike.core.util.ClassDeactivationUtils;
+
+
+/**
+ * Registers the @{link WindowIdRenderKit}
+ */
+public class WindowIdRenderKitFactory extends RenderKitFactory implements Deactivatable
+{
+ private final RenderKitFactory wrapped;
+
+ private final boolean deactivated;
+
+ /**
+ * Constructor for wrapping the given {@link javax.faces.render.RenderKitFactory}
+ * @param wrapped render-kit-factory which will be wrapped
+ */
+ public WindowIdRenderKitFactory(RenderKitFactory wrapped)
+ {
+ this.wrapped = wrapped;
+ this.deactivated = !isActivated();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addRenderKit(String s, RenderKit renderKit)
+ {
+ wrapped.addRenderKit(s, renderKit);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public RenderKit getRenderKit(FacesContext facesContext, String s)
+ {
+ RenderKit renderKit = wrapped.getRenderKit(facesContext, s);
+
+ if (renderKit == null)
+ {
+ return null;
+ }
+
+ if (deactivated)
+ {
+ return renderKit;
+ }
+
+ return new WindowIdRenderKitWrapper(renderKit);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator<String> getRenderKitIds()
+ {
+ return wrapped.getRenderKitIds();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public RenderKitFactory getWrapped()
+ {
+ return wrapped;
+ }
+
+ public boolean isActivated()
+ {
+ return ClassDeactivationUtils.isActivated(getClass());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdRenderKitWrapper.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdRenderKitWrapper.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdRenderKitWrapper.java
new file mode 100644
index 0000000..bb939d5
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/scope/window/WindowIdRenderKitWrapper.java
@@ -0,0 +1,92 @@
+/*
+ * 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.deltaspike.jsf.impl.scope.window;
+
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitWrapper;
+import java.io.Writer;
+
+import org.apache.deltaspike.core.api.provider.BeanProvider;
+import org.apache.deltaspike.core.spi.scope.window.WindowContext;
+
+/**
+ * Wraps the RenderKit and adds the
+ * {@link WindowIdHolderComponent} to the view tree
+ */
+public class WindowIdRenderKitWrapper extends RenderKitWrapper
+{
+ private final RenderKit wrapped;
+
+ /**
+ * This will get initialized lazily to prevent boot order issues
+ * with the JSF and CDI containers.
+ */
+ private volatile WindowContext windowContext;
+
+
+ //needed if the renderkit gets proxied - see EXTCDI-215
+ protected WindowIdRenderKitWrapper()
+ {
+ this.wrapped = null;
+ }
+
+ public WindowIdRenderKitWrapper(RenderKit wrapped)
+ {
+ this.wrapped = wrapped;
+ }
+
+ @Override
+ public RenderKit getWrapped()
+ {
+ return wrapped;
+ }
+
+ /**
+ * Adds a {@link WindowIdHolderComponent} with the
+ * current windowId to the component tree.
+ */
+ public ResponseWriter createResponseWriter(Writer writer, String s, String s1)
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ String windowId = getWindowContext().getCurrentWindowId();
+
+ WindowIdHolderComponent.addWindowIdHolderComponent(facesContext, windowId);
+
+ return wrapped.createResponseWriter(writer, s, s1);
+ }
+
+
+ private WindowContext getWindowContext()
+ {
+ if (windowContext == null)
+ {
+ synchronized (this)
+ {
+ if (windowContext == null)
+ {
+ windowContext = BeanProvider.getContextualReference(WindowContext.class);
+ }
+ }
+ }
+
+ return windowContext;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/30661b05/deltaspike/modules/jsf/impl/src/main/resources/META-INF/faces-config.xml
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/resources/META-INF/faces-config.xml b/deltaspike/modules/jsf/impl/src/main/resources/META-INF/faces-config.xml
index 50ed167..6d951da 100644
--- a/deltaspike/modules/jsf/impl/src/main/resources/META-INF/faces-config.xml
+++ b/deltaspike/modules/jsf/impl/src/main/resources/META-INF/faces-config.xml
@@ -36,5 +36,6 @@
<factory>
<lifecycle-factory>org.apache.deltaspike.jsf.impl.listener.request.DeltaSpikeLifecycleFactoryWrapper</lifecycle-factory>
<faces-context-factory>org.apache.deltaspike.jsf.impl.listener.request.DeltaSpikeFacesContextFactory</faces-context-factory>
+ <render-kit-factory>org.apache.deltaspike.jsf.impl.scope.window.WindowIdRenderKitFactory</render-kit-factory>
</factory>
-</faces-config>
\ No newline at end of file
+</faces-config>