You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ta...@apache.org on 2021/03/05 17:17:12 UTC
[myfaces] branch master updated: refactored CDI contexts (flow
still missing)
This is an automated email from the ASF dual-hosted git repository.
tandraschko pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces.git
The following commit(s) were added to refs/heads/master by this push:
new 8012a70 refactored CDI contexts (flow still missing)
8012a70 is described below
commit 8012a70d2135da47f25585a0d5f9e777c306e3c3
Author: Thomas Andraschko <ta...@apache.org>
AuthorDate: Fri Mar 5 18:17:05 2021 +0100
refactored CDI contexts (flow still missing)
---
.../viewstate/SerializedViewCollection.java | 604 ++++++++++-----------
.../cdi/clientwindow/ClientWindowScopeContext.java | 452 ++++++---------
.../ClientWindowScopeContextualStorageHolder.java | 59 ++
.../cdi/util/AbstractContextualStorageHolder.java | 270 +++++++++
.../apache/myfaces/cdi/util/ContextualStorage.java | 27 +-
.../myfaces/cdi/view/ViewScopeBeanHolder.java | 239 --------
.../apache/myfaces/cdi/view/ViewScopeCDIMap.java | 54 +-
.../apache/myfaces/cdi/view/ViewScopeContext.java | 535 ++++++++----------
.../cdi/view/ViewScopeContextualStorage.java | 18 -
.../cdi/view/ViewScopeContextualStorageHolder.java | 59 ++
.../myfaces/cdi/view/ViewScopeExtension.java | 2 +-
.../org/apache/myfaces/view/ViewScopeProxyMap.java | 453 ++++++++--------
.../myfaces/webapp/MyFacesHttpSessionListener.java | 194 +++----
13 files changed, 1426 insertions(+), 1540 deletions(-)
diff --git a/impl/src/main/java/org/apache/myfaces/application/viewstate/SerializedViewCollection.java b/impl/src/main/java/org/apache/myfaces/application/viewstate/SerializedViewCollection.java
index bd049b4..8eababb 100644
--- a/impl/src/main/java/org/apache/myfaces/application/viewstate/SerializedViewCollection.java
+++ b/impl/src/main/java/org/apache/myfaces/application/viewstate/SerializedViewCollection.java
@@ -1,302 +1,302 @@
-/*
- * 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.application.viewstate;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Logger;
-import jakarta.faces.context.FacesContext;
-import java.util.function.Consumer;
-import org.apache.myfaces.cdi.view.ViewScopeContext;
-import org.apache.myfaces.config.MyfacesConfig;
-import org.apache.myfaces.util.lang.LRULinkedHashMap;
-
-/**
- *
- */
-class SerializedViewCollection implements Serializable
-{
- private static final Logger log = Logger.getLogger(SerializedViewCollection.class.getName());
-
- private static final Object[] EMPTY_STATES = new Object[]{null, null};
-
- private static final long serialVersionUID = -3734849062185115847L;
-
- private final List<SerializedViewKey> _keys = new ArrayList<>(MyfacesConfig.NUMBER_OF_VIEWS_IN_SESSION_DEFAULT);
- private final Map<SerializedViewKey, Object> _serializedViews = new HashMap<>();
-
- /**
- * The viewScopeIds can be shared between multiple entries of the same
- * view. To store it into session, the best is use two maps, one to
- * associate the view key with the view scope id and other to keep track
- * of the number of times the id is used. In that way it is possible to
- * know when a view scope id has been discarded and destroy the view scope
- * in the right time.
- */
- private HashMap<SerializedViewKey, String> _viewScopeIds = null;
- private HashMap<String, Integer> _viewScopeIdCounts = null;
-
- private final Map<SerializedViewKey, SerializedViewKey> _precedence = new HashMap<>();
- private Map<String, SerializedViewKey> _lastWindowKeys = null;
-
- public void put(FacesContext context, Object state, SerializedViewKey key, SerializedViewKey previousRestoredKey)
- {
- put(context, state, key, previousRestoredKey, null,
- (oldViewScopeId) -> ViewScopeContext.destroyAllActive(context, oldViewScopeId));
- }
-
- public synchronized void put(FacesContext context, Object state,
- SerializedViewKey key, SerializedViewKey previousRestoredKey, String viewScopeId)
- {
- put(context, state, key, previousRestoredKey, viewScopeId,
- (oldViewScopeId) -> ViewScopeContext.destroyAllActive(context, oldViewScopeId));
- }
-
- public synchronized void put(FacesContext context, Object state,
- SerializedViewKey key, SerializedViewKey previousRestoredKey, String viewScopeId,
- Consumer<String> destroyCallback)
- {
- if (state == null)
- {
- state = EMPTY_STATES;
- }
- else if (state instanceof Object[] &&
- ((Object[])state).length == 2 &&
- ((Object[])state)[0] == null &&
- ((Object[])state)[1] == null)
- {
- // The generated state can be considered zero, set it as null
- // into the map.
- state = null;
- }
-
- if (_serializedViews.containsKey(key))
- {
- // Update the state, the viewScopeId does not change.
- _serializedViews.put(key, state);
- // Make sure the view is at the end of the discard queue
- while (_keys.remove(key))
- {
- // do nothing
- }
- _keys.add(key);
- return;
- }
-
- Integer maxCount = getNumberOfSequentialViewsInSession(context);
- if (maxCount != null)
- {
- if (previousRestoredKey != null)
- {
- if (!_serializedViews.isEmpty())
- {
- _precedence.put((SerializedViewKey) key, previousRestoredKey);
- }
- else
- {
- // Note when the session is invalidated, _serializedViews map is empty,
- // but we could have a not null previousRestoredKey (the last one before
- // invalidate the session), so we need to check that condition before
- // set the precence. In that way, we ensure the precedence map will always
- // have valid keys.
- previousRestoredKey = null;
- }
- }
- }
- _serializedViews.put(key, state);
-
- if (viewScopeId != null)
- {
- if (_viewScopeIds == null)
- {
- _viewScopeIds = new HashMap<>();
- }
- _viewScopeIds.put(key, viewScopeId);
- if (_viewScopeIdCounts == null)
- {
- _viewScopeIdCounts = new HashMap<>();
- }
- Integer vscount = _viewScopeIdCounts.get(viewScopeId);
- vscount = (vscount == null) ? 1 : vscount + 1;
- _viewScopeIdCounts.put(viewScopeId, vscount);
- }
-
- while (_keys.remove(key))
- {
- // do nothing
- }
- _keys.add(key);
-
- if (previousRestoredKey != null && maxCount != null && maxCount > 0)
- {
- int count = 0;
- SerializedViewKey previousKey = (SerializedViewKey) key;
- do
- {
- previousKey = _precedence.get(previousKey);
- count++;
- }
- while (previousKey != null && count < maxCount);
-
- if (previousKey != null)
- {
- SerializedViewKey keyToRemove = (SerializedViewKey) previousKey;
- // In theory it should be only one key but just to be sure
- // do it in a loop, but in this case if cache old views is on,
- // put on that map.
- do
- {
- while (_keys.remove(keyToRemove))
- {
- // do nothing
- }
-
- _serializedViews.remove(keyToRemove);
-
- if (_viewScopeIds != null)
- {
- String oldViewScopeId = _viewScopeIds.remove(keyToRemove);
- if (oldViewScopeId != null)
- {
- Integer vscount = _viewScopeIdCounts.get(oldViewScopeId);
- vscount = vscount - 1;
- if (vscount < 1)
- {
- _viewScopeIdCounts.remove(oldViewScopeId);
- destroyCallback.accept(oldViewScopeId);
- }
- else
- {
- _viewScopeIdCounts.put(oldViewScopeId, vscount);
- }
- }
- }
-
- keyToRemove = _precedence.remove(keyToRemove);
- }
- while (keyToRemove != null);
- }
- }
- int views = getNumberOfViewsInSession(context);
- while (_keys.size() > views)
- {
- key = _keys.remove(0);
- if (maxCount != null && maxCount > 0)
- {
- SerializedViewKey keyToRemove = (SerializedViewKey) key;
- // Note in this case the key to delete is the oldest one,
- // so it could be at least one precedence, but to be safe
- // do it with a loop.
- do
- {
- keyToRemove = _precedence.remove(keyToRemove);
- }
- while (keyToRemove != null);
- }
-
- _serializedViews.remove(key);
-
- if (_viewScopeIds != null)
- {
- String oldViewScopeId = _viewScopeIds.remove(key);
- if (oldViewScopeId != null)
- {
- Integer vscount = _viewScopeIdCounts.get(oldViewScopeId);
- vscount = vscount - 1;
- if (vscount < 1)
- {
- _viewScopeIdCounts.remove(oldViewScopeId);
- destroyCallback.accept(oldViewScopeId);
- }
- else
- {
- _viewScopeIdCounts.put(oldViewScopeId, vscount);
- }
- }
- }
- }
- }
-
- protected Integer getNumberOfSequentialViewsInSession(FacesContext context)
- {
- return MyfacesConfig.getCurrentInstance(context).getNumberOfSequentialViewsInSession();
- }
-
- /**
- * Reads the amount (default = 20) of views to be stored in session.
- * @see ServerSideStateCacheImpl#NUMBER_OF_VIEWS_IN_SESSION_PARAM
- * @param context FacesContext for the current request, we are processing
- * @return Number vf views stored in the session
- */
- protected int getNumberOfViewsInSession(FacesContext context)
- {
- return MyfacesConfig.getCurrentInstance(context).getNumberOfViewsInSession();
- }
-
- public synchronized void putLastWindowKey(FacesContext context, String id, SerializedViewKey key)
- {
- if (_lastWindowKeys == null)
- {
- Integer i = getNumberOfSequentialViewsInSession(context);
- int j = getNumberOfViewsInSession(context);
- if (i != null && i> 0)
- {
- _lastWindowKeys = new LRULinkedHashMap<>((j / i) + 1);
- }
- else
- {
- _lastWindowKeys = new LRULinkedHashMap(j + 1);
- }
- }
- _lastWindowKeys.put(id, key);
- }
-
- public SerializedViewKey getLastWindowKey(FacesContext context, String id)
- {
- if (_lastWindowKeys != null)
- {
- return _lastWindowKeys.get(id);
- }
- return null;
- }
-
- public Object get(SerializedViewKey key)
- {
- Object value = _serializedViews.get(key);
- if (value == null)
- {
- if (_serializedViews.containsKey(key))
- {
- return EMPTY_STATES;
- }
- }
- else if (value instanceof Object[] &&
- ((Object[])value).length == 2 &&
- ((Object[])value)[0] == null &&
- ((Object[])value)[1] == null)
- {
- // Remember inside the state map null is stored as an empty array.
- return null;
- }
- return value;
- }
-}
+/*
+ * 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.application.viewstate;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+import jakarta.faces.context.FacesContext;
+import java.util.function.Consumer;
+import org.apache.myfaces.cdi.view.ViewScopeContext;
+import org.apache.myfaces.config.MyfacesConfig;
+import org.apache.myfaces.util.lang.LRULinkedHashMap;
+
+/**
+ *
+ */
+class SerializedViewCollection implements Serializable
+{
+ private static final Logger log = Logger.getLogger(SerializedViewCollection.class.getName());
+
+ private static final Object[] EMPTY_STATES = new Object[]{null, null};
+
+ private static final long serialVersionUID = -3734849062185115847L;
+
+ private final List<SerializedViewKey> _keys = new ArrayList<>(MyfacesConfig.NUMBER_OF_VIEWS_IN_SESSION_DEFAULT);
+ private final Map<SerializedViewKey, Object> _serializedViews = new HashMap<>();
+
+ /**
+ * The viewScopeIds can be shared between multiple entries of the same
+ * view. To store it into session, the best is use two maps, one to
+ * associate the view key with the view scope id and other to keep track
+ * of the number of times the id is used. In that way it is possible to
+ * know when a view scope id has been discarded and destroy the view scope
+ * in the right time.
+ */
+ private HashMap<SerializedViewKey, String> _viewScopeIds = null;
+ private HashMap<String, Integer> _viewScopeIdCounts = null;
+
+ private final Map<SerializedViewKey, SerializedViewKey> _precedence = new HashMap<>();
+ private Map<String, SerializedViewKey> _lastWindowKeys = null;
+
+ public void put(FacesContext context, Object state, SerializedViewKey key, SerializedViewKey previousRestoredKey)
+ {
+ put(context, state, key, previousRestoredKey, null,
+ (oldViewScopeId) -> ViewScopeContext.destroyAll(context, oldViewScopeId));
+ }
+
+ public synchronized void put(FacesContext context, Object state,
+ SerializedViewKey key, SerializedViewKey previousRestoredKey, String viewScopeId)
+ {
+ put(context, state, key, previousRestoredKey, viewScopeId,
+ (oldViewScopeId) -> ViewScopeContext.destroyAll(context, oldViewScopeId));
+ }
+
+ public synchronized void put(FacesContext context, Object state,
+ SerializedViewKey key, SerializedViewKey previousRestoredKey, String viewScopeId,
+ Consumer<String> destroyCallback)
+ {
+ if (state == null)
+ {
+ state = EMPTY_STATES;
+ }
+ else if (state instanceof Object[] &&
+ ((Object[])state).length == 2 &&
+ ((Object[])state)[0] == null &&
+ ((Object[])state)[1] == null)
+ {
+ // The generated state can be considered zero, set it as null
+ // into the map.
+ state = null;
+ }
+
+ if (_serializedViews.containsKey(key))
+ {
+ // Update the state, the viewScopeId does not change.
+ _serializedViews.put(key, state);
+ // Make sure the view is at the end of the discard queue
+ while (_keys.remove(key))
+ {
+ // do nothing
+ }
+ _keys.add(key);
+ return;
+ }
+
+ Integer maxCount = getNumberOfSequentialViewsInSession(context);
+ if (maxCount != null)
+ {
+ if (previousRestoredKey != null)
+ {
+ if (!_serializedViews.isEmpty())
+ {
+ _precedence.put((SerializedViewKey) key, previousRestoredKey);
+ }
+ else
+ {
+ // Note when the session is invalidated, _serializedViews map is empty,
+ // but we could have a not null previousRestoredKey (the last one before
+ // invalidate the session), so we need to check that condition before
+ // set the precence. In that way, we ensure the precedence map will always
+ // have valid keys.
+ previousRestoredKey = null;
+ }
+ }
+ }
+ _serializedViews.put(key, state);
+
+ if (viewScopeId != null)
+ {
+ if (_viewScopeIds == null)
+ {
+ _viewScopeIds = new HashMap<>();
+ }
+ _viewScopeIds.put(key, viewScopeId);
+ if (_viewScopeIdCounts == null)
+ {
+ _viewScopeIdCounts = new HashMap<>();
+ }
+ Integer vscount = _viewScopeIdCounts.get(viewScopeId);
+ vscount = (vscount == null) ? 1 : vscount + 1;
+ _viewScopeIdCounts.put(viewScopeId, vscount);
+ }
+
+ while (_keys.remove(key))
+ {
+ // do nothing
+ }
+ _keys.add(key);
+
+ if (previousRestoredKey != null && maxCount != null && maxCount > 0)
+ {
+ int count = 0;
+ SerializedViewKey previousKey = (SerializedViewKey) key;
+ do
+ {
+ previousKey = _precedence.get(previousKey);
+ count++;
+ }
+ while (previousKey != null && count < maxCount);
+
+ if (previousKey != null)
+ {
+ SerializedViewKey keyToRemove = (SerializedViewKey) previousKey;
+ // In theory it should be only one key but just to be sure
+ // do it in a loop, but in this case if cache old views is on,
+ // put on that map.
+ do
+ {
+ while (_keys.remove(keyToRemove))
+ {
+ // do nothing
+ }
+
+ _serializedViews.remove(keyToRemove);
+
+ if (_viewScopeIds != null)
+ {
+ String oldViewScopeId = _viewScopeIds.remove(keyToRemove);
+ if (oldViewScopeId != null)
+ {
+ Integer vscount = _viewScopeIdCounts.get(oldViewScopeId);
+ vscount = vscount - 1;
+ if (vscount < 1)
+ {
+ _viewScopeIdCounts.remove(oldViewScopeId);
+ destroyCallback.accept(oldViewScopeId);
+ }
+ else
+ {
+ _viewScopeIdCounts.put(oldViewScopeId, vscount);
+ }
+ }
+ }
+
+ keyToRemove = _precedence.remove(keyToRemove);
+ }
+ while (keyToRemove != null);
+ }
+ }
+ int views = getNumberOfViewsInSession(context);
+ while (_keys.size() > views)
+ {
+ key = _keys.remove(0);
+ if (maxCount != null && maxCount > 0)
+ {
+ SerializedViewKey keyToRemove = (SerializedViewKey) key;
+ // Note in this case the key to delete is the oldest one,
+ // so it could be at least one precedence, but to be safe
+ // do it with a loop.
+ do
+ {
+ keyToRemove = _precedence.remove(keyToRemove);
+ }
+ while (keyToRemove != null);
+ }
+
+ _serializedViews.remove(key);
+
+ if (_viewScopeIds != null)
+ {
+ String oldViewScopeId = _viewScopeIds.remove(key);
+ if (oldViewScopeId != null)
+ {
+ Integer vscount = _viewScopeIdCounts.get(oldViewScopeId);
+ vscount = vscount - 1;
+ if (vscount < 1)
+ {
+ _viewScopeIdCounts.remove(oldViewScopeId);
+ destroyCallback.accept(oldViewScopeId);
+ }
+ else
+ {
+ _viewScopeIdCounts.put(oldViewScopeId, vscount);
+ }
+ }
+ }
+ }
+ }
+
+ protected Integer getNumberOfSequentialViewsInSession(FacesContext context)
+ {
+ return MyfacesConfig.getCurrentInstance(context).getNumberOfSequentialViewsInSession();
+ }
+
+ /**
+ * Reads the amount (default = 20) of views to be stored in session.
+ * @see ServerSideStateCacheImpl#NUMBER_OF_VIEWS_IN_SESSION_PARAM
+ * @param context FacesContext for the current request, we are processing
+ * @return Number vf views stored in the session
+ */
+ protected int getNumberOfViewsInSession(FacesContext context)
+ {
+ return MyfacesConfig.getCurrentInstance(context).getNumberOfViewsInSession();
+ }
+
+ public synchronized void putLastWindowKey(FacesContext context, String id, SerializedViewKey key)
+ {
+ if (_lastWindowKeys == null)
+ {
+ Integer i = getNumberOfSequentialViewsInSession(context);
+ int j = getNumberOfViewsInSession(context);
+ if (i != null && i> 0)
+ {
+ _lastWindowKeys = new LRULinkedHashMap<>((j / i) + 1);
+ }
+ else
+ {
+ _lastWindowKeys = new LRULinkedHashMap(j + 1);
+ }
+ }
+ _lastWindowKeys.put(id, key);
+ }
+
+ public SerializedViewKey getLastWindowKey(FacesContext context, String id)
+ {
+ if (_lastWindowKeys != null)
+ {
+ return _lastWindowKeys.get(id);
+ }
+ return null;
+ }
+
+ public Object get(SerializedViewKey key)
+ {
+ Object value = _serializedViews.get(key);
+ if (value == null)
+ {
+ if (_serializedViews.containsKey(key))
+ {
+ return EMPTY_STATES;
+ }
+ }
+ else if (value instanceof Object[] &&
+ ((Object[])value).length == 2 &&
+ ((Object[])value)[0] == null &&
+ ((Object[])value)[1] == null)
+ {
+ // Remember inside the state map null is stored as an empty array.
+ return null;
+ }
+ return value;
+ }
+}
diff --git a/impl/src/main/java/org/apache/myfaces/cdi/clientwindow/ClientWindowScopeContext.java b/impl/src/main/java/org/apache/myfaces/cdi/clientwindow/ClientWindowScopeContext.java
index d4aa201..8bb9d15 100644
--- a/impl/src/main/java/org/apache/myfaces/cdi/clientwindow/ClientWindowScopeContext.java
+++ b/impl/src/main/java/org/apache/myfaces/cdi/clientwindow/ClientWindowScopeContext.java
@@ -1,287 +1,165 @@
-/*
- * 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.cdi.clientwindow;
-
-import java.lang.annotation.Annotation;
-import java.util.Map;
-import jakarta.enterprise.context.ContextNotActiveException;
-import jakarta.enterprise.context.spi.Context;
-import jakarta.enterprise.context.spi.Contextual;
-import jakarta.enterprise.context.spi.CreationalContext;
-import jakarta.enterprise.inject.Typed;
-import jakarta.enterprise.inject.spi.BeanManager;
-import jakarta.faces.context.FacesContext;
-import jakarta.faces.lifecycle.ClientWindowScoped;
-import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
-import org.apache.myfaces.cdi.util.ContextualInstanceInfo;
-import org.apache.myfaces.cdi.util.ContextualStorage;
-
-/**
- * Minimal implementation of ClientWindowScope.
- */
-@Typed()
-public class ClientWindowScopeContext implements Context
-{
- public static final String CLIENT_WINDOW_SCOPE_MAP = "oam.CLIENT_WINDOW_SCOPE_MAP";
-
- private BeanManager beanManager;
-
- public ClientWindowScopeContext(BeanManager beanManager)
- {
- this.beanManager = beanManager;
- }
-
- /**
- * An implementation has to return the underlying storage which
- * contains the items held in the Context.
- *
- * @param createIfNotExist whether a ContextualStorage shall get created if it doesn't yet exist.
- * @param facesContext
- *
- * @return the underlying storage
- */
- protected ContextualStorage getContextualStorage(boolean createIfNotExist, FacesContext facesContext)
- {
- if (facesContext == null)
- {
- throw new ContextNotActiveException(this.getClass().getName() + ": no current active FacesContext");
- }
-
- Map<String, Object> sessionMap =
- facesContext.getExternalContext().getSessionMap();
- Map<String, ContextualStorage> contextualStorageMap =
- (Map<String, ContextualStorage>) sessionMap.get(CLIENT_WINDOW_SCOPE_MAP);
- if (contextualStorageMap == null)
- {
- if (!createIfNotExist)
- {
- return null;
- }
-
- contextualStorageMap = new ConcurrentHashMap<>();
- sessionMap.put(CLIENT_WINDOW_SCOPE_MAP, contextualStorageMap);
- }
-
- String clientWindowId = getCurrentClientWindowId();
- ContextualStorage contextualStorage = contextualStorageMap.get(clientWindowId);
- if (contextualStorage == null)
- {
- if (!createIfNotExist)
- {
- return null;
- }
-
- contextualStorage = new ContextualStorage(beanManager, false);
- contextualStorageMap.put(clientWindowId, contextualStorage);
- }
-
- return contextualStorage;
- }
-
- @Override
- public Class<? extends Annotation> getScope()
- {
- return ClientWindowScoped.class;
- }
-
- @Override
- public boolean isActive()
- {
- return isActive(FacesContext.getCurrentInstance());
- }
-
- public boolean isActive(FacesContext facesContext)
- {
- if (facesContext == null || facesContext.getExternalContext().getClientWindow() == null)
- {
- return false;
- }
-
- return true;
- }
-
- protected String getCurrentClientWindowId()
- {
- return FacesContext.getCurrentInstance().getExternalContext().getClientWindow().getId();
- }
-
- @Override
- public <T> T get(Contextual<T> bean)
- {
- FacesContext facesContext = FacesContext.getCurrentInstance();
-
- checkActive(facesContext);
-
- if (facesContext != null)
- {
- ContextualStorage storage = getContextualStorage(false, facesContext);
- if (storage != null)
- {
- Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
- ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
-
- if (contextualInstanceInfo != null)
- {
- return (T) contextualInstanceInfo.getContextualInstance();
- }
- }
- }
- else
- {
- throw new IllegalStateException("FacesContext cannot be found when resolving bean " + bean.toString());
- }
- return null;
- }
-
- @Override
- public <T> T get(Contextual<T> bean, CreationalContext<T> creationalContext)
- {
- FacesContext facesContext = FacesContext.getCurrentInstance();
-
- checkActive(facesContext);
-
- ContextualStorage storage = getContextualStorage(true, facesContext);
-
- Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
- ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
-
- if (contextualInstanceInfo != null)
- {
- @SuppressWarnings("unchecked")
- final T instance = (T) contextualInstanceInfo.getContextualInstance();
-
- if (instance != null)
- {
- return instance;
- }
- }
-
- return storage.createContextualInstance(bean, creationalContext);
- }
-
- /**
- * Destroy the Contextual Instance of the given Bean.
- * @param bean dictates which bean shall get cleaned up
- * @return <code>true</code> if the bean was destroyed, <code>false</code> if there was no such bean.
- */
- public boolean destroy(Contextual bean)
- {
- FacesContext facesContext = FacesContext.getCurrentInstance();
- ContextualStorage storage = getContextualStorage(false, facesContext);
- if (storage == null)
- {
- return false;
- }
- ContextualInstanceInfo<?> contextualInstanceInfo = storage.getStorage().get(storage.getBeanKey(bean));
-
- if (contextualInstanceInfo == null)
- {
- return false;
- }
-
- bean.destroy(contextualInstanceInfo.getContextualInstance(), contextualInstanceInfo.getCreationalContext());
- return true;
- }
-
- /**
- * Make sure that the context is really active.
- *
- * @param facesContext the current {@link FacesContext}.
- * @throws ContextNotActiveException if there is no active context for the current thread.
- */
- protected void checkActive(FacesContext facesContext)
- {
- if (!isActive(facesContext))
- {
- throw new ContextNotActiveException("CDI context with scope annotation @"
- + getScope().getName() + " is not active with respect to the current thread");
- }
- }
-
- public static void onSessionDestroyed(FacesContext facesContext)
- {
- destroyAllActive(facesContext);
- }
-
- public static void destroyAllActive(FacesContext facesContext)
- {
- if (facesContext == null)
- {
- return;
- }
-
- Map<String, Object> sessionMap =
- facesContext.getExternalContext().getSessionMap();
- Map<String, ContextualStorage> contextualStorageMap =
- (Map<String, ContextualStorage>) sessionMap.get(CLIENT_WINDOW_SCOPE_MAP);
- if (contextualStorageMap == null || contextualStorageMap.isEmpty())
- {
- return;
- }
-
- Iterator<String> iterator = contextualStorageMap.keySet().iterator();
- while (iterator.hasNext())
- {
- String clientWindowId = iterator.next();
- iterator.remove();
-
- ContextualStorage contextualStorage = contextualStorageMap.get(clientWindowId);
- destroyAllActive(facesContext, contextualStorage);
- }
- }
-
- public static void destroyAllActive(FacesContext facesContext, String clientWindowId)
- {
- if (facesContext == null)
- {
- return;
- }
-
- Map<String, Object> sessionMap =
- facesContext.getExternalContext().getSessionMap();
- Map<String, ContextualStorage> contextualStorageMap =
- (Map<String, ContextualStorage>) sessionMap.get(CLIENT_WINDOW_SCOPE_MAP);
- if (contextualStorageMap == null || contextualStorageMap.isEmpty())
- {
- return;
- }
-
- ContextualStorage contextualStorage = contextualStorageMap.remove(clientWindowId);
- destroyAllActive(facesContext, contextualStorage);
- }
-
- protected static void destroyAllActive(FacesContext facesContext, ContextualStorage contextualStorage)
- {
- if (contextualStorage == null)
- {
- return;
- }
-
- Map<Object, ContextualInstanceInfo<?>> contextMap = contextualStorage.getStorage();
- for (Map.Entry<Object, ContextualInstanceInfo<?>> entry : contextMap.entrySet())
- {
- Contextual bean = contextualStorage.getBean(entry.getKey());
-
- ContextualInstanceInfo<?> contextualInstanceInfo = entry.getValue();
- bean.destroy(contextualInstanceInfo.getContextualInstance(),
- contextualInstanceInfo.getCreationalContext());
- }
- }
-}
+/*
+ * 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.cdi.clientwindow;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import jakarta.enterprise.context.ContextNotActiveException;
+import jakarta.enterprise.context.spi.Context;
+import jakarta.enterprise.context.spi.Contextual;
+import jakarta.enterprise.context.spi.CreationalContext;
+import jakarta.enterprise.inject.Typed;
+import jakarta.enterprise.inject.spi.BeanManager;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.lifecycle.ClientWindowScoped;
+import org.apache.myfaces.cdi.util.ContextualInstanceInfo;
+import org.apache.myfaces.cdi.util.ContextualStorage;
+import org.apache.myfaces.cdi.util.AbstractContextualStorageHolder;
+
+/**
+ * Minimal implementation of ClientWindowScope.
+ */
+@Typed()
+public class ClientWindowScopeContext implements Context
+{
+ private BeanManager beanManager;
+
+ public ClientWindowScopeContext(BeanManager beanManager)
+ {
+ this.beanManager = beanManager;
+ }
+
+ @Override
+ public Class<? extends Annotation> getScope()
+ {
+ return ClientWindowScoped.class;
+ }
+
+ @Override
+ public boolean isActive()
+ {
+ return isActive(FacesContext.getCurrentInstance());
+ }
+
+ public boolean isActive(FacesContext facesContext)
+ {
+ if (facesContext == null || facesContext.getExternalContext().getClientWindow() == null)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public <T> T get(Contextual<T> bean)
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+
+ checkActive(facesContext);
+
+ if (facesContext != null)
+ {
+ ContextualStorage storage = getContextManager(facesContext).getContextualStorage(
+ getCurrentClientWindowId(facesContext), false);
+ if (storage != null)
+ {
+ Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
+ ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
+
+ if (contextualInstanceInfo != null)
+ {
+ return (T) contextualInstanceInfo.getContextualInstance();
+ }
+ }
+ }
+ else
+ {
+ throw new IllegalStateException("FacesContext cannot be found when resolving bean " + bean.toString());
+ }
+ return null;
+ }
+
+ @Override
+ public <T> T get(Contextual<T> bean, CreationalContext<T> creationalContext)
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+
+ checkActive(facesContext);
+
+ ContextualStorage storage = getContextManager(facesContext).getContextualStorage(
+ getCurrentClientWindowId(facesContext), true);
+
+ Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
+ ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
+
+ if (contextualInstanceInfo != null)
+ {
+ @SuppressWarnings("unchecked")
+ final T instance = (T) contextualInstanceInfo.getContextualInstance();
+
+ if (instance != null)
+ {
+ return instance;
+ }
+ }
+
+ return storage.createContextualInstance(bean, creationalContext);
+ }
+
+ protected void checkActive(FacesContext facesContext)
+ {
+ if (!isActive(facesContext))
+ {
+ throw new ContextNotActiveException("CDI context with scope annotation @"
+ + getScope().getName() + " is not active with respect to the current thread");
+ }
+ }
+
+ protected ClientWindowScopeContextualStorageHolder getContextManager(FacesContext context)
+ {
+ return AbstractContextualStorageHolder.getInstance(context, ClientWindowScopeContextualStorageHolder.class);
+ }
+
+ protected String getCurrentClientWindowId(FacesContext context)
+ {
+ return context.getExternalContext().getClientWindow().getId();
+ }
+
+
+ public static void destroyAll(FacesContext facesContext)
+ {
+ ClientWindowScopeContextualStorageHolder manager = AbstractContextualStorageHolder.getInstance(facesContext,
+ ClientWindowScopeContextualStorageHolder.class);
+ if (manager != null)
+ {
+ manager.destroyAll(facesContext);
+ }
+ }
+
+ public static void destroyAll(FacesContext context, String clientWindowId)
+ {
+ ClientWindowScopeContextualStorageHolder manager = AbstractContextualStorageHolder.getInstance(context,
+ ClientWindowScopeContextualStorageHolder.class);
+ if (manager != null)
+ {
+ manager.destroyAll(context, clientWindowId);
+ }
+ }
+}
diff --git a/impl/src/main/java/org/apache/myfaces/cdi/clientwindow/ClientWindowScopeContextualStorageHolder.java b/impl/src/main/java/org/apache/myfaces/cdi/clientwindow/ClientWindowScopeContextualStorageHolder.java
new file mode 100644
index 0000000..a12a323
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/cdi/clientwindow/ClientWindowScopeContextualStorageHolder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.cdi.clientwindow;
+
+import jakarta.enterprise.context.SessionScoped;
+import jakarta.enterprise.context.spi.Contextual;
+import jakarta.faces.context.FacesContext;
+import java.io.Serializable;
+import java.util.Map;
+import org.apache.myfaces.cdi.util.ContextualInstanceInfo;
+import org.apache.myfaces.cdi.util.ContextualStorage;
+import org.apache.myfaces.cdi.util.AbstractContextualStorageHolder;
+
+@SessionScoped
+public class ClientWindowScopeContextualStorageHolder
+ extends AbstractContextualStorageHolder<ContextualStorage>
+ implements Serializable
+{
+ @Override
+ public void destroyAll(ContextualStorage contextualStorage, FacesContext facesContext)
+ {
+ if (contextualStorage == null)
+ {
+ return;
+ }
+
+ Map<Object, ContextualInstanceInfo<?>> contextMap = contextualStorage.getStorage();
+ for (Map.Entry<Object, ContextualInstanceInfo<?>> entry : contextMap.entrySet())
+ {
+ Contextual bean = contextualStorage.getBean(entry.getKey());
+
+ ContextualInstanceInfo<?> contextualInstanceInfo = entry.getValue();
+ bean.destroy(contextualInstanceInfo.getContextualInstance(),
+ contextualInstanceInfo.getCreationalContext());
+ }
+ }
+
+ @Override
+ protected ContextualStorage newContextualStorage(String slotId)
+ {
+ return new ContextualStorage(beanManager, true);
+ }
+}
diff --git a/impl/src/main/java/org/apache/myfaces/cdi/util/AbstractContextualStorageHolder.java b/impl/src/main/java/org/apache/myfaces/cdi/util/AbstractContextualStorageHolder.java
new file mode 100644
index 0000000..88a6155
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/cdi/util/AbstractContextualStorageHolder.java
@@ -0,0 +1,270 @@
+/*
+ * 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.cdi.util;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
+import jakarta.enterprise.context.spi.Contextual;
+import jakarta.enterprise.inject.spi.BeanManager;
+import jakarta.faces.context.ExceptionHandler;
+import jakarta.faces.context.ExternalContext;
+import jakarta.faces.context.FacesContext;
+import jakarta.inject.Inject;
+import jakarta.servlet.ServletContext;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.myfaces.cdi.JsfApplicationArtifactHolder;
+import org.apache.myfaces.context.ExceptionHandlerImpl;
+import org.apache.myfaces.context.servlet.StartupFacesContextImpl;
+import org.apache.myfaces.context.servlet.StartupServletExternalContextImpl;
+
+public abstract class AbstractContextualStorageHolder<T extends ContextualStorage> implements Serializable
+{
+ @Inject
+ protected JsfApplicationArtifactHolder applicationContextBean;
+
+ @Inject
+ protected BeanManager beanManager;
+
+ protected Map<String, T> storageMap;
+
+ public AbstractContextualStorageHolder()
+ {
+ }
+
+ @PostConstruct
+ public void init()
+ {
+ storageMap = new ConcurrentHashMap<>();
+
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+
+ Object context = facesContext.getExternalContext().getContext();
+ if (context instanceof ServletContext)
+ {
+ JsfApplicationArtifactHolder appBean = CDIUtils.get(beanManager, JsfApplicationArtifactHolder.class);
+ if (appBean.getServletContext() != null)
+ {
+ appBean.setServletContext((ServletContext) context);
+ }
+ }
+ }
+
+ /**
+ *
+ * This method will replace the storageMap and with a new empty one.
+ * This method can be used to properly destroy the BeanHolder beans without having to sync heavily.
+ * Any {@link jakarta.enterprise.inject.spi.Bean#destroy(Object, jakarta.enterprise.context.spi.CreationalContext)}
+ * should be performed on the returned old storage map.
+ *
+ * @return the old storageMap.
+ */
+ public Map<String, T> forceNewStorage()
+ {
+ Map<String, T> oldStorageMap = storageMap;
+ storageMap = new ConcurrentHashMap<>();
+ return oldStorageMap;
+ }
+
+ public Map<String, T> getStorageMap()
+ {
+ return storageMap;
+ }
+
+ public T getContextualStorage(String slotId)
+ {
+ return getContextualStorage(slotId, true);
+ }
+
+ public T getContextualStorage(String slotId, boolean create)
+ {
+ if (storageMap == null)
+ {
+ if (!create)
+ {
+ return null;
+ }
+
+ storageMap = new ConcurrentHashMap<>();
+ }
+
+ T storage = storageMap.get(slotId);
+ if (storage == null && create)
+ {
+ storage = newContextualStorage(slotId);
+ storageMap.put(slotId, storage);
+ }
+ return storage;
+ }
+
+ protected abstract T newContextualStorage(String slotId);
+
+ @PreDestroy
+ public void preDestroy()
+ {
+ // After some testing done two things are clear:
+ // 1. jetty + weld call @PreDestroy at the end of the request
+ // 2. use a HttpServletListener in tomcat + owb does not work, because
+ // CDI listener is executed first.
+ // So we need a mixed approach using both a listener and @PreDestroy annotations.
+ // When the first one in being called replace the storages with a new map
+ // and call PreDestroy, when the second one is called, it founds an empty map
+ // and the process stops. A hack to get ServletContext from CDI is required to
+ // provide a valid FacesContext instance.
+ Map<String, T> oldContextStorages = forceNewStorage();
+ if (!oldContextStorages.isEmpty())
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ if (facesContext == null && applicationContextBean.getServletContext() != null)
+ {
+ try
+ {
+ ServletContext servletContext = applicationContextBean.getServletContext();
+ ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, false);
+ ExceptionHandler exceptionHandler = new ExceptionHandlerImpl();
+ facesContext = new StartupFacesContextImpl(externalContext,
+ externalContext, exceptionHandler, false);
+ for (T contextualStorage : oldContextStorages.values())
+ {
+ destroyAll(contextualStorage, facesContext);
+ }
+ }
+ finally
+ {
+ facesContext.release();
+ }
+ }
+ else
+ {
+ for (T contextualStorage : oldContextStorages.values())
+ {
+ destroyAll(contextualStorage, facesContext);
+ }
+ }
+ }
+ }
+
+ public void destroyAll(FacesContext facesContext)
+ {
+ if (storageMap == null || storageMap.isEmpty())
+ {
+ return;
+ }
+
+ // we replace the old BeanHolder beans with a new storage Map
+ // an afterwards destroy the old Beans without having to care about any syncs.
+ // This behavior also helps as a check to avoid destroy the same beans twice.
+ Map<String, T> oldContextStorages = forceNewStorage();
+
+ for (T contextualStorage : oldContextStorages.values())
+ {
+ destroyAll(contextualStorage, facesContext);
+ }
+ }
+
+ public void destroyAll(T contextualStorage, FacesContext facesContext)
+ {
+ if (facesContext == null)
+ {
+ facesContext = FacesContext.getCurrentInstance();
+ }
+
+ boolean tempFacesContext = false;
+ if (facesContext == null && applicationContextBean.getServletContext() != null)
+ {
+ ServletContext servletContext = applicationContextBean.getServletContext();
+ ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, false);
+ ExceptionHandler exceptionHandler = new ExceptionHandlerImpl();
+ facesContext = new StartupFacesContextImpl(externalContext, externalContext, exceptionHandler, false);
+ tempFacesContext = true;
+ }
+
+ try
+ {
+ Map<Object, ContextualInstanceInfo<?>> contextMap = contextualStorage.getStorage();
+
+ for (Map.Entry<Object, ContextualInstanceInfo<?>> entry : contextMap.entrySet())
+ {
+ Contextual bean = contextualStorage.getBean(entry.getKey());
+
+ ContextualInstanceInfo<?> contextualInstanceInfo = entry.getValue();
+ bean.destroy(contextualInstanceInfo.getContextualInstance(),
+ contextualInstanceInfo.getCreationalContext());
+ }
+
+ contextMap.clear();
+
+ contextualStorage.deactivate();
+ }
+ finally
+ {
+ if (tempFacesContext)
+ {
+ facesContext.release();
+ }
+ }
+ }
+
+ public void destroyAll(FacesContext context, String slotId)
+ {
+ if (storageMap == null || storageMap.isEmpty())
+ {
+ return;
+ }
+
+ T contextualStorage = storageMap.remove(slotId);
+ destroyAll(contextualStorage, context);
+ }
+
+ public static <T extends AbstractContextualStorageHolder> T getInstance(FacesContext facesContext,
+ Class<T> contextManagerClass)
+ {
+ if (facesContext == null
+ || facesContext.getExternalContext() == null
+ || facesContext.getExternalContext().getSession(false) == null)
+ {
+ return null;
+ }
+
+ BeanManager beanManager = CDIUtils.getBeanManager(facesContext.getExternalContext());
+ if (beanManager == null)
+ {
+ return null;
+ }
+
+ if (!CDIUtils.isSessionScopeActive(beanManager))
+ {
+ return null;
+ }
+
+ T cached = (T) facesContext.getExternalContext().getSessionMap().get(contextManagerClass.getClass().getName());
+ if (cached == null)
+ {
+ cached = CDIUtils.getOptional(beanManager, contextManagerClass);
+ if (cached != null)
+ {
+ facesContext.getExternalContext().getSessionMap().put(contextManagerClass.getClass().getName(),
+ cached);
+ }
+ }
+
+ return cached;
+ }
+}
diff --git a/impl/src/main/java/org/apache/myfaces/cdi/util/ContextualStorage.java b/impl/src/main/java/org/apache/myfaces/cdi/util/ContextualStorage.java
index 1537733..6aac5e4 100644
--- a/impl/src/main/java/org/apache/myfaces/cdi/util/ContextualStorage.java
+++ b/impl/src/main/java/org/apache/myfaces/cdi/util/ContextualStorage.java
@@ -39,10 +39,16 @@ public class ContextualStorage implements Serializable
{
private static final long serialVersionUID = 1L;
- protected final Map<Object, ContextualInstanceInfo<?>> contextualInstances;
- protected final BeanManager beanManager;
- protected final boolean concurrent;
+ protected Map<Object, ContextualInstanceInfo<?>> contextualInstances;
+ protected BeanManager beanManager;
+ protected boolean concurrent;
+ protected transient volatile boolean activated;
+ public ContextualStorage()
+ {
+ this.activated = true;
+ }
+
/**
* @param beanManager is needed for serialisation
* @param concurrent whether the ContextualStorage might get accessed concurrently by different threads
@@ -59,6 +65,7 @@ public class ContextualStorage implements Serializable
{
contextualInstances = new HashMap<>();
}
+ this.activated = true;
}
/**
@@ -164,4 +171,18 @@ public class ContextualStorage implements Serializable
return (Contextual<?>) beanKey;
}
+ public boolean isActivated()
+ {
+ return activated;
+ }
+
+ public void activate()
+ {
+ activated = true;
+ }
+
+ public void deactivate()
+ {
+ activated = false;
+ }
}
diff --git a/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeBeanHolder.java b/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeBeanHolder.java
deleted file mode 100644
index 980b975..0000000
--- a/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeBeanHolder.java
+++ /dev/null
@@ -1,239 +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.myfaces.cdi.view;
-
-import org.apache.myfaces.cdi.JsfApplicationArtifactHolder;
-import java.io.Serializable;
-import java.util.Map;
-import java.util.Random;
-import java.util.concurrent.ConcurrentHashMap;
-import jakarta.annotation.PostConstruct;
-import jakarta.annotation.PreDestroy;
-import jakarta.enterprise.context.SessionScoped;
-import jakarta.enterprise.inject.spi.BeanManager;
-import jakarta.faces.context.ExceptionHandler;
-import jakarta.faces.context.ExternalContext;
-import jakarta.faces.context.FacesContext;
-import jakarta.inject.Inject;
-import jakarta.servlet.ServletContext;
-import org.apache.myfaces.cdi.util.CDIUtils;
-import org.apache.myfaces.context.servlet.StartupFacesContextImpl;
-import org.apache.myfaces.context.servlet.StartupServletExternalContextImpl;
-import org.apache.myfaces.context.ExceptionHandlerImpl;
-
-/**
- *
- * @author Leonardo Uribe
- */
-@SessionScoped
-public class ViewScopeBeanHolder implements Serializable
-{
- /**
- * key: the windowId for the browser tab or window
- * value: the {@link ViewScopeContextualStorage} which holds all the
- * {@link jakarta.enterprise.inject.spi.Bean}s.
- */
- private Map<String, ViewScopeContextualStorage> storageMap;
-
- private static final Random RANDOM_GENERATOR = new Random();
-
- public static final String CREATED = ViewScopeBeanHolder.class.getName() + ".CREATED";
-
- @Inject
- JsfApplicationArtifactHolder applicationContextBean;
-
- public ViewScopeBeanHolder()
- {
- }
-
- @PostConstruct
- public void init()
- {
- storageMap = new ConcurrentHashMap<>();
- FacesContext facesContext = FacesContext.getCurrentInstance();
- facesContext.getExternalContext().getSessionMap().put(CREATED, true);
-
- Object context = facesContext.getExternalContext().getContext();
- if (context instanceof ServletContext)
- {
- BeanManager beanManager = CDIUtils.getBeanManager(facesContext.getExternalContext());
- JsfApplicationArtifactHolder appBean = CDIUtils.get(beanManager, JsfApplicationArtifactHolder.class);
- if (appBean.getServletContext() != null)
- {
- appBean.setServletContext((ServletContext) context);
- }
- }
- }
-
- /**
- * This method will return the ViewScopeContextualStorage or create a new one
- * if no one is yet assigned to the current windowId.
- *
- * @param beanManager
- * @param viewScopeId
- * @return
- */
- public ViewScopeContextualStorage getContextualStorage(BeanManager beanManager, String viewScopeId)
- {
- ViewScopeContextualStorage storage = storageMap.get(viewScopeId);
- if (storage == null)
- {
- storage = new ViewScopeContextualStorage(beanManager);
- storageMap.put(viewScopeId, storage);
- }
- return storage;
- }
-
- public Map<String, ViewScopeContextualStorage> getStorageMap()
- {
- return storageMap;
- }
-
- /**
- *
- * This method will replace the storageMap and with a new empty one.
- * This method can be used to properly destroy the BeanHolder beans without having to sync heavily.
- * Any {@link jakarta.enterprise.inject.spi.Bean#destroy(Object, jakarta.enterprise.context.spi.CreationalContext)}
- * should be performed on the returned old storage map.
- *
- * @return the old storageMap.
- */
- public Map<String, ViewScopeContextualStorage> forceNewStorage()
- {
- Map<String, ViewScopeContextualStorage> oldStorageMap = storageMap;
- storageMap = new ConcurrentHashMap<>();
- 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 @ViewScoped beans.
- */
- //@PreDestroy
- public void destroyBeans()
- {
- // we replace the old BeanHolder beans with a new storage Map
- // an afterwards destroy the old Beans without having to care about any syncs.
- // This behavior also helps as a check to avoid destroy the same beans twice.
- Map<String, ViewScopeContextualStorage> oldContextStorages = forceNewStorage();
-
- for (ViewScopeContextualStorage contextualStorage : oldContextStorages.values())
- {
- ViewScopeContext.destroyAllActive(contextualStorage);
- }
- }
-
- public void destroyBeans(String viewScopeId)
- {
- ViewScopeContextualStorage contextualStorage = storageMap.get(viewScopeId);
- if (contextualStorage != null)
- {
- try
- {
- FacesContext facesContext = FacesContext.getCurrentInstance();
- if (facesContext == null && applicationContextBean.getServletContext() != null)
- {
- try
- {
- ServletContext servletContext = applicationContextBean.getServletContext();
- ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, false);
- ExceptionHandler exceptionHandler = new ExceptionHandlerImpl();
- facesContext = new StartupFacesContextImpl(externalContext,
- externalContext, exceptionHandler, false);
- ViewScopeContext.destroyAllActive(contextualStorage, facesContext);
- }
- finally
- {
- facesContext.release();
- }
- }
- else
- {
- ViewScopeContext.destroyAllActive(contextualStorage, facesContext);
- }
- }
- finally
- {
- //remove the viewScopeId to prevent memory leak
- storageMap.remove(viewScopeId);
- }
- }
- }
-
- @PreDestroy
- public void destroyBeansOnPreDestroy()
- {
- // After some testing done two things are clear:
- // 1. jetty + weld call @PreDestroy at the end of the request
- // 2. use a HttpServletListener in tomcat + owb does not work, because
- // CDI listener is executed first.
- // So we need a mixed approach using both a listener and @PreDestroy annotations.
- // When the first one in being called replace the storages with a new map
- // and call PreDestroy, when the second one is called, it founds an empty map
- // and the process stops. A hack to get ServletContext from CDI is required to
- // provide a valid FacesContext instance.
- Map<String, ViewScopeContextualStorage> oldContextStorages = forceNewStorage();
- if (!oldContextStorages.isEmpty())
- {
- FacesContext facesContext = FacesContext.getCurrentInstance();
- if (facesContext == null && applicationContextBean.getServletContext() != null)
- {
- try
- {
- ServletContext servletContext = applicationContextBean.getServletContext();
- ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, false);
- ExceptionHandler exceptionHandler = new ExceptionHandlerImpl();
- facesContext = new StartupFacesContextImpl(externalContext,
- externalContext, exceptionHandler, false);
- for (ViewScopeContextualStorage contextualStorage : oldContextStorages.values())
- {
- ViewScopeContext.destroyAllActive(contextualStorage, facesContext);
- }
- }
- finally
- {
- facesContext.release();
- }
- }
- else
- {
- for (ViewScopeContextualStorage contextualStorage : oldContextStorages.values())
- {
- ViewScopeContext.destroyAllActive(contextualStorage);
- }
- }
- }
- }
-
- public String generateUniqueViewScopeId()
- {
- // To ensure uniqueness we just use a random generator and we check
- // if the key is already used.
- String key;
- do
- {
- key = Integer.toString(RANDOM_GENERATOR.nextInt());
- } while (storageMap.containsKey(key));
- return key;
- }
-
-}
diff --git a/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeCDIMap.java b/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeCDIMap.java
index 8107645..e3874f1 100644
--- a/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeCDIMap.java
+++ b/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeCDIMap.java
@@ -35,32 +35,19 @@ import org.apache.myfaces.cdi.util.ContextualInstanceInfo;
*/
public class ViewScopeCDIMap implements Map<String, Object>
{
- private String _viewScopeId;
-
+ private String viewScopeId;
private ViewScopeContextualStorage storage;
public ViewScopeCDIMap(FacesContext facesContext)
{
BeanManager beanManager = CDIUtils.getBeanManager(facesContext.getExternalContext());
-
- ViewScopeBeanHolder bean = CDIUtils.get(beanManager, ViewScopeBeanHolder.class);
-
- // 1. get a new view scope id
- _viewScopeId = bean.generateUniqueViewScopeId();
-
- storage = bean.getContextualStorage(beanManager, _viewScopeId);
+ ViewScopeContextualStorageHolder bean = CDIUtils.get(beanManager, ViewScopeContextualStorageHolder.class);
+ viewScopeId = bean.generateUniqueViewScopeId();
}
public ViewScopeCDIMap(FacesContext facesContext, String viewScopeId)
{
- BeanManager beanManager = CDIUtils.getBeanManager(facesContext.getExternalContext());
-
- ViewScopeBeanHolder bean = CDIUtils.get(beanManager, ViewScopeBeanHolder.class);
-
- // 1. get a new view scope id
- _viewScopeId = viewScopeId;
-
- storage = bean.getContextualStorage(beanManager, _viewScopeId);
+ this.viewScopeId = viewScopeId;
}
private ViewScopeContextualStorage getStorage()
@@ -74,9 +61,9 @@ public class ViewScopeCDIMap implements Map<String, Object>
FacesContext facesContext = FacesContext.getCurrentInstance();
BeanManager beanManager = CDIUtils.getBeanManager(facesContext.getExternalContext());
- ViewScopeBeanHolder bean = CDIUtils.get(beanManager, ViewScopeBeanHolder.class);
+ ViewScopeContextualStorageHolder bean = CDIUtils.get(beanManager, ViewScopeContextualStorageHolder.class);
- storage = bean.getContextualStorage(beanManager, _viewScopeId);
+ storage = bean.getContextualStorage(viewScopeId);
}
return storage;
}
@@ -93,9 +80,9 @@ public class ViewScopeCDIMap implements Map<String, Object>
public String getViewScopeId()
{
- return _viewScopeId;
+ return viewScopeId;
}
-
+
@Override
public int size()
{
@@ -172,34 +159,11 @@ public class ViewScopeCDIMap implements Map<String, Object>
@Override
public void clear()
{
- boolean destroyed = false;
// If the scope was already destroyed through an invalidateSession(), the storage instance
// that is holding this map could be obsolete, so we need to grab the right instance from
// the bean holder.
FacesContext facesContext = FacesContext.getCurrentInstance();
- if (facesContext != null)
- {
- BeanManager beanManager = CDIUtils.getBeanManager(facesContext.getExternalContext());
-
- if (beanManager != null)
- {
- ViewScopeBeanHolder bean = CDIUtils.get(beanManager, ViewScopeBeanHolder.class);
- if (bean != null)
- {
- ViewScopeContextualStorage st = bean.getContextualStorage(beanManager, _viewScopeId);
- if (st != null)
- {
- ViewScopeContext.destroyAllActive(st);
- storage = null;
- destroyed = true;
- }
- }
- }
- }
- if (!destroyed)
- {
- ViewScopeContext.destroyAllActive(storage);
- }
+ ViewScopeContext.destroyAll(facesContext, viewScopeId);
}
@Override
diff --git a/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeContext.java b/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeContext.java
index 12fc8d2..e5826eb 100644
--- a/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeContext.java
+++ b/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeContext.java
@@ -1,323 +1,214 @@
-/*
- * 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.cdi.view;
-
-import jakarta.enterprise.context.ContextNotActiveException;
-import jakarta.enterprise.inject.Typed;
-import jakarta.enterprise.inject.spi.BeanManager;
-
-import java.lang.annotation.Annotation;
-import java.util.Map;
-import jakarta.enterprise.context.spi.Context;
-import jakarta.enterprise.context.spi.Contextual;
-import jakarta.enterprise.context.spi.CreationalContext;
-import jakarta.enterprise.inject.spi.PassivationCapable;
-import jakarta.faces.context.FacesContext;
-import jakarta.faces.view.ViewScoped;
-
-import org.apache.myfaces.cdi.util.CDIUtils;
-import org.apache.myfaces.cdi.util.ContextualInstanceInfo;
-import org.apache.myfaces.view.ViewScopeProxyMap;
-
-/**
- * CDI Context to handle @{@link ViewScoped} beans.
- *
- * @author Leonardo Uribe
- */
-@Typed()
-public class ViewScopeContext implements Context
-{
-
- /**
- * needed for serialisation and passivationId
- */
- private BeanManager beanManager;
-
- private boolean passivatingScope;
-
- public ViewScopeContext(BeanManager beanManager)
- {
- this.beanManager = beanManager;
- this.passivatingScope = beanManager.isPassivatingScope(getScope());
- }
-
- protected ViewScopeBeanHolder getOrCreateViewScopeBeanHolder()
- {
- FacesContext facesContext = FacesContext.getCurrentInstance();
- ViewScopeBeanHolder beanHolder = (ViewScopeBeanHolder) facesContext.getExternalContext().getSessionMap()
- .get(ViewScopeBeanHolder.class.getName());
- if (beanHolder == null)
- {
- beanHolder = CDIUtils.get(beanManager, ViewScopeBeanHolder.class);
- facesContext.getExternalContext().getSessionMap().put(
- ViewScopeBeanHolder.class.getName(),
- beanHolder);
- }
-
- return beanHolder;
- }
-
- protected static ViewScopeBeanHolder getViewScopeBeanHolder(FacesContext facesContext)
- {
- return (ViewScopeBeanHolder) facesContext.getExternalContext().getSessionMap()
- .get(ViewScopeBeanHolder.class.getName());
- }
-
- public String getCurrentViewScopeId(boolean create)
- {
- FacesContext facesContext = FacesContext.getCurrentInstance();
- ViewScopeProxyMap map = (ViewScopeProxyMap) facesContext.getViewRoot().getViewMap(create);
- if (map != null)
- {
- String id = map.getViewScopeId();
- if (id == null && create)
- {
- // Force create
- map.forceCreateWrappedMap(facesContext);
- id = map.getViewScopeId();
- }
- return id;
- }
- return null;
- }
-
- protected ViewScopeContextualStorage getContextualStorage(boolean createIfNotExist)
- {
- String viewScopeId = getCurrentViewScopeId(createIfNotExist);
- if (createIfNotExist && viewScopeId == null)
- {
- throw new ContextNotActiveException(
- this.getClass().getSimpleName() + ": no viewScopeId set for the current view yet!");
- }
- if (viewScopeId != null)
- {
- return getOrCreateViewScopeBeanHolder().getContextualStorage(beanManager, viewScopeId);
- }
- return null;
- }
-
- @Override
- public Class<? extends Annotation> getScope()
- {
- return ViewScoped.class;
- }
-
- /**
- * The WindowContext is active once a current windowId is set for the current Thread.
- * @return
- */
- @Override
- public boolean isActive()
- {
- FacesContext facesContext = FacesContext.getCurrentInstance();
- if (facesContext != null)
- {
- return facesContext.getViewRoot() != null;
- }
- else
- {
- // No FacesContext means no view scope active.
- return false;
- }
- }
-
- @Override
- public <T> T get(Contextual<T> bean)
- {
- checkActive();
-
- // force session creation if ViewScoped is used
- FacesContext.getCurrentInstance().getExternalContext().getSession(true);
-
- ViewScopeContextualStorage storage = getContextualStorage(false);
- if (storage == null)
- {
- return null;
- }
-
- Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
- ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
- if (contextualInstanceInfo == null)
- {
- return null;
- }
-
- return (T) contextualInstanceInfo.getContextualInstance();
- }
-
- @Override
- public <T> T get(Contextual<T> bean, CreationalContext<T> creationalContext)
- {
- checkActive();
-
- if (passivatingScope && !(bean instanceof PassivationCapable))
- {
- throw new IllegalStateException(bean.toString() +
- " doesn't implement " + PassivationCapable.class.getName());
- }
-
- // force session creation if ViewScoped is used
- FacesContext.getCurrentInstance().getExternalContext().getSession(true);
-
- ViewScopeContextualStorage storage = getContextualStorage(true);
-
- Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
- ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
-
- if (contextualInstanceInfo != null)
- {
- @SuppressWarnings("unchecked")
- final T instance = (T) contextualInstanceInfo.getContextualInstance();
- if (instance != null)
- {
- return instance;
- }
- }
-
- return storage.createContextualInstance(bean, creationalContext);
- }
-
- /**
- * Destroy the Contextual Instance of the given Bean.
- * @param bean dictates which bean shall get cleaned up
- * @return <code>true</code> if the bean was destroyed, <code>false</code> if there was no such bean.
- */
- public boolean destroy(Contextual bean)
- {
- ViewScopeContextualStorage storage = getContextualStorage(false);
- if (storage == null)
- {
- return false;
- }
-
- ContextualInstanceInfo<?> contextualInstanceInfo = storage.getStorage().get(storage.getBeanKey(bean));
- if (contextualInstanceInfo == null)
- {
- return false;
- }
-
- bean.destroy(contextualInstanceInfo.getContextualInstance(),
- contextualInstanceInfo.getCreationalContext());
-
- return true;
- }
-
- /**
- * destroys all the Contextual Instances in the Storage returned by
- * {@link #getContextualStorage(boolean)}.
- */
- public void destroyAllActive()
- {
- ViewScopeContextualStorage storage = getContextualStorage(false);
- if (storage == null)
- {
- return;
- }
-
- destroyAllActive(storage);
- }
-
-
- public static void destroyAllActive(FacesContext context, String viewScopeId)
- {
- if (isViewScopeBeanHolderCreated(context))
- {
- ViewScopeBeanHolder beanHolder = getViewScopeBeanHolder(context);
- if (beanHolder != null)
- {
- beanHolder.destroyBeans(viewScopeId);
- }
- }
- }
-
-
- public static void destroyAllActive(ViewScopeContextualStorage storage)
- {
- destroyAllActive(storage, FacesContext.getCurrentInstance());
- }
-
- public static void destroyAllActive(ViewScopeContextualStorage storage, FacesContext facesContext)
- {
- Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
-
- for (Map.Entry<Object, ContextualInstanceInfo<?>> entry : contextMap.entrySet())
- {
- if (!(entry.getKey() instanceof ViewScopeContextualKey))
- {
- Contextual bean = storage.getBean(entry.getKey());
-
- ContextualInstanceInfo<?> contextualInstanceInfo = entry.getValue();
- bean.destroy(contextualInstanceInfo.getContextualInstance(),
- contextualInstanceInfo.getCreationalContext());
- }
- }
-
- contextMap.clear();
-
- storage.deactivate();
- }
-
- /**
- * Make sure that the Context is really active.
- * @throws ContextNotActiveException if there is no active
- * Context for the current Thread.
- */
- protected void checkActive()
- {
- if (!isActive())
- {
- throw new ContextNotActiveException("CDI context with scope annotation @"
- + getScope().getName() + " is not active with respect to the current thread");
- }
- }
-
- private static boolean isViewScopeBeanHolderCreated(FacesContext facesContext)
- {
- if (facesContext.getExternalContext().getSession(false) == null)
- {
- return false;
- }
-
- return facesContext.getExternalContext().
- getSessionMap().containsKey(ViewScopeBeanHolder.CREATED);
- }
-
- public static void onSessionDestroyed(FacesContext facesContext)
- {
- if (facesContext == null)
- {
- return;
- }
-
- // In CDI case, the best way to deal with this is use a method
- // with @PreDestroy annotation on a session scope bean
- // ( ViewScopeBeanHolder.destroyBeans() ). There is no need
- // to do anything else in this location, but it is advised
- // in CDI the beans are destroyed at the end of the request,
- // not when invalidateSession() is called.
- if (isViewScopeBeanHolderCreated(facesContext))
- {
- ViewScopeBeanHolder beanHolder = getViewScopeBeanHolder(facesContext);
- if (beanHolder != null)
- {
- beanHolder.destroyBeans();
- }
- }
- }
+/*
+ * 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.cdi.view;
+
+import jakarta.enterprise.context.ContextNotActiveException;
+import jakarta.enterprise.inject.Typed;
+import jakarta.enterprise.inject.spi.BeanManager;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import jakarta.enterprise.context.spi.Context;
+import jakarta.enterprise.context.spi.Contextual;
+import jakarta.enterprise.context.spi.CreationalContext;
+import jakarta.enterprise.inject.spi.PassivationCapable;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.view.ViewScoped;
+
+import org.apache.myfaces.cdi.util.ContextualInstanceInfo;
+import org.apache.myfaces.cdi.util.AbstractContextualStorageHolder;
+import org.apache.myfaces.view.ViewScopeProxyMap;
+
+/**
+ * CDI Context to handle @{@link ViewScoped} beans.
+ *
+ * @author Leonardo Uribe
+ */
+@Typed()
+public class ViewScopeContext implements Context
+{
+
+ /**
+ * needed for serialisation and passivationId
+ */
+ private BeanManager beanManager;
+
+ private boolean passivatingScope;
+
+ public ViewScopeContext(BeanManager beanManager)
+ {
+ this.beanManager = beanManager;
+ this.passivatingScope = beanManager.isPassivatingScope(getScope());
+ }
+
+ protected ViewScopeContextualStorageHolder getContextManager(FacesContext facesContext)
+ {
+ return AbstractContextualStorageHolder.getInstance(facesContext, ViewScopeContextualStorageHolder.class);
+ }
+
+ protected static ViewScopeContextualStorageHolder getViewScopeBeanHolder(FacesContext facesContext)
+ {
+ return (ViewScopeContextualStorageHolder) facesContext.getExternalContext().getSessionMap()
+ .get(ViewScopeContextualStorageHolder.class.getName());
+ }
+
+ public String getCurrentViewScopeId(boolean create)
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ ViewScopeProxyMap map = (ViewScopeProxyMap) facesContext.getViewRoot().getViewMap(create);
+ if (map != null)
+ {
+ String id = map.getViewScopeId();
+ if (id == null && create)
+ {
+ // Force create
+ map.forceCreateWrappedMap(facesContext);
+ id = map.getViewScopeId();
+ }
+ return id;
+ }
+ return null;
+ }
+
+ protected ViewScopeContextualStorage getContextualStorage(FacesContext facesContext, boolean createIfNotExist)
+ {
+ String viewScopeId = getCurrentViewScopeId(createIfNotExist);
+ if (createIfNotExist && viewScopeId == null)
+ {
+ throw new ContextNotActiveException(
+ this.getClass().getSimpleName() + ": no viewScopeId set for the current view yet!");
+ }
+ if (viewScopeId != null)
+ {
+ return getContextManager(facesContext).getContextualStorage(viewScopeId, createIfNotExist);
+ }
+ return null;
+ }
+
+ @Override
+ public Class<? extends Annotation> getScope()
+ {
+ return ViewScoped.class;
+ }
+
+ @Override
+ public boolean isActive()
+ {
+ return isActive(FacesContext.getCurrentInstance());
+ }
+
+ public boolean isActive(FacesContext facesContext)
+ {
+ if (facesContext == null || facesContext.getViewRoot() == null)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public <T> T get(Contextual<T> bean)
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+
+ checkActive(facesContext);
+
+ // force session creation if ViewScoped is used
+ facesContext.getExternalContext().getSession(true);
+
+ ViewScopeContextualStorage storage = getContextualStorage(facesContext, false);
+ if (storage == null)
+ {
+ return null;
+ }
+
+ Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
+ ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
+ if (contextualInstanceInfo == null)
+ {
+ return null;
+ }
+
+ return (T) contextualInstanceInfo.getContextualInstance();
+ }
+
+ @Override
+ public <T> T get(Contextual<T> bean, CreationalContext<T> creationalContext)
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+
+ checkActive(facesContext);
+
+ if (passivatingScope && !(bean instanceof PassivationCapable))
+ {
+ throw new IllegalStateException(bean.toString() +
+ " doesn't implement " + PassivationCapable.class.getName());
+ }
+
+ // force session creation if ViewScoped is used
+ facesContext.getExternalContext().getSession(true);
+
+ ViewScopeContextualStorage storage = getContextualStorage(facesContext, true);
+
+ Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
+ ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
+
+ if (contextualInstanceInfo != null)
+ {
+ @SuppressWarnings("unchecked")
+ final T instance = (T) contextualInstanceInfo.getContextualInstance();
+ if (instance != null)
+ {
+ return instance;
+ }
+ }
+
+ return storage.createContextualInstance(bean, creationalContext);
+ }
+
+ protected void checkActive(FacesContext facesContext)
+ {
+ if (!isActive(facesContext))
+ {
+ throw new ContextNotActiveException("CDI context with scope annotation @"
+ + getScope().getName() + " is not active with respect to the current thread");
+ }
+ }
+
+ public static void destroyAll(FacesContext facesContext)
+ {
+ ViewScopeContextualStorageHolder manager = AbstractContextualStorageHolder.getInstance(facesContext,
+ ViewScopeContextualStorageHolder.class);
+ if (manager != null)
+ {
+ manager.destroyAll(facesContext);
+ }
+ }
+
+ public static void destroyAll(FacesContext facesContext, String viewScopeId)
+ {
+ ViewScopeContextualStorageHolder manager = AbstractContextualStorageHolder.getInstance(facesContext,
+ ViewScopeContextualStorageHolder.class);
+ if (manager != null)
+ {
+ manager.destroyAll(facesContext, viewScopeId);
+ }
+ }
}
\ No newline at end of file
diff --git a/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeContextualStorage.java b/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeContextualStorage.java
index 636972f..d9c52e6 100644
--- a/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeContextualStorage.java
+++ b/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeContextualStorage.java
@@ -38,13 +38,10 @@ public class ViewScopeContextualStorage extends ContextualStorage
private final Map<String, Object> nameBeanKeyMap;
- private transient volatile boolean activated;
-
public ViewScopeContextualStorage(BeanManager beanManager)
{
super(beanManager, false);
this.nameBeanKeyMap = new HashMap<>();
- this.activated = true;
}
public Map<String, Object> getNameBeanKeyMap()
@@ -68,19 +65,4 @@ public class ViewScopeContextualStorage extends ContextualStorage
return instance;
}
-
- public boolean isActivated()
- {
- return activated;
- }
-
- public void activate()
- {
- activated = true;
- }
-
- public void deactivate()
- {
- activated = false;
- }
}
diff --git a/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeContextualStorageHolder.java b/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeContextualStorageHolder.java
new file mode 100644
index 0000000..abe80b9
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeContextualStorageHolder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.cdi.view;
+
+import java.io.Serializable;
+import java.util.Random;
+import jakarta.enterprise.context.SessionScoped;
+import org.apache.myfaces.cdi.util.AbstractContextualStorageHolder;
+
+/**
+ *
+ * @author Leonardo Uribe
+ */
+@SessionScoped
+public class ViewScopeContextualStorageHolder
+ extends AbstractContextualStorageHolder<ViewScopeContextualStorage>
+ implements Serializable
+{
+ private static final Random RANDOM_GENERATOR = new Random();
+
+ public ViewScopeContextualStorageHolder()
+ {
+ }
+
+ public String generateUniqueViewScopeId()
+ {
+ // To ensure uniqueness we just use a random generator and we check
+ // if the key is already used.
+ String key;
+ do
+ {
+ key = Integer.toString(RANDOM_GENERATOR.nextInt());
+ } while (storageMap.containsKey(key));
+ return key;
+ }
+
+ @Override
+ protected ViewScopeContextualStorage newContextualStorage(String slotId)
+ {
+ return new ViewScopeContextualStorage(beanManager);
+ }
+
+}
diff --git a/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeExtension.java b/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeExtension.java
index 6b764bd..78e7ab5 100644
--- a/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeExtension.java
+++ b/impl/src/main/java/org/apache/myfaces/cdi/view/ViewScopeExtension.java
@@ -40,7 +40,7 @@ public class ViewScopeExtension implements Extension
event.addScope(ViewScoped.class, true, true);
// Register ViewScopeBeanHolder as a bean with CDI annotations, so the system
// can take it into account, and use it later when necessary.
- AnnotatedType bean = beanManager.createAnnotatedType(ViewScopeBeanHolder.class);
+ AnnotatedType bean = beanManager.createAnnotatedType(ViewScopeContextualStorageHolder.class);
event.addAnnotatedType(bean, bean.getJavaClass().getName());
}
diff --git a/impl/src/main/java/org/apache/myfaces/view/ViewScopeProxyMap.java b/impl/src/main/java/org/apache/myfaces/view/ViewScopeProxyMap.java
index 715491e..6ea0762 100644
--- a/impl/src/main/java/org/apache/myfaces/view/ViewScopeProxyMap.java
+++ b/impl/src/main/java/org/apache/myfaces/view/ViewScopeProxyMap.java
@@ -1,226 +1,227 @@
-/*
- * 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.view;
-
-import jakarta.enterprise.inject.spi.BeanManager;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import jakarta.faces.component.StateHolder;
-import jakarta.faces.context.FacesContext;
-import jakarta.faces.event.PreDestroyViewMapEvent;
-import org.apache.myfaces.cdi.util.CDIUtils;
-import org.apache.myfaces.cdi.view.ViewScopeBeanHolder;
-import org.apache.myfaces.cdi.view.ViewScopeCDIMap;
-import org.apache.myfaces.util.ExternalSpecifications;
-
-/**
- * This wrapper has these objectives:
- *
- * - Isolate the part that needs to be saved with the view (viewScopeId) from
- * the part that should remain into session (bean map). This class will be
- * serialized when UIViewRoot.saveState() is called.
- * - Decouple the way how the view scope map is stored. For example, in
- * CDI view scope a session scope bean is used, and in default view scope
- * the same session map is used but using a prefix.
- *
- * @author Leonardo Uribe
- */
-public class ViewScopeProxyMap extends HashMap<String, Object> implements StateHolder
-{
- private String _viewScopeId;
-
- private transient Map<String, Object> _delegate;
-
- public ViewScopeProxyMap()
- {
- }
-
-
- public String getViewScopeId()
- {
- return _viewScopeId;
- }
-
- public void forceCreateWrappedMap(FacesContext facesContext)
- {
- getWrapped();
- }
-
- public Map<String, Object> getWrapped()
- {
- if (_delegate == null)
- {
- FacesContext facesContext = FacesContext.getCurrentInstance();
-
- if (facesContext != null)
- {
- // for unittests without CDI
- if (!ExternalSpecifications.isCDIAvailable(facesContext.getExternalContext()))
- {
- _delegate = new ViewScope();
- return _delegate;
- }
-
- if (_viewScopeId == null)
- {
- BeanManager beanManager = CDIUtils.getBeanManager(facesContext.getExternalContext());
- ViewScopeBeanHolder beanHolder = CDIUtils.get(beanManager, ViewScopeBeanHolder.class);
- _viewScopeId = beanHolder.generateUniqueViewScopeId();
- }
- _delegate = new ViewScopeCDIMap(facesContext, _viewScopeId);
- }
- else
- {
- // In junit test cases, where there is no facesContext instance, it is enough to
- // just get a blank instance.
- _delegate = new ViewScope();
- }
- }
- return _delegate;
- }
-
- @Override
- public int size()
- {
- return getWrapped().size();
- }
-
- @Override
- public boolean isEmpty()
- {
- return getWrapped().isEmpty();
- }
-
- @Override
- public boolean containsKey(Object key)
- {
- return getWrapped().containsKey(key);
- }
-
- @Override
- public boolean containsValue(Object value)
- {
- return getWrapped().containsValue(value);
- }
-
- @Override
- public Object get(Object key)
- {
- return getWrapped().get(key);
- }
-
- @Override
- public Object put(String key, Object value)
- {
- return getWrapped().put(key, value);
- }
-
- @Override
- public Object remove(Object key)
- {
- return getWrapped().remove(key);
- }
-
- @Override
- public void putAll(Map<? extends String, ? extends Object> m)
- {
- getWrapped().putAll(m);
- }
-
- @Override
- public void clear()
- {
- /*
- * The returned Map must be implemented such that calling clear() on the Map causes
- * Application.publishEvent(java.lang.Class, java.lang.Object) to be called, passing
- * ViewMapDestroyedEvent.class as the first argument and this UIViewRoot instance as the second argument.
- */
- FacesContext facesContext = FacesContext.getCurrentInstance();
- facesContext.getApplication().publishEvent(facesContext,
- PreDestroyViewMapEvent.class, facesContext.getViewRoot());
-
- getWrapped().clear();
- }
-
- @Override
- public Set<String> keySet()
- {
- return getWrapped().keySet();
- }
-
- @Override
- public Collection<Object> values()
- {
- return getWrapped().values();
- }
-
- @Override
- public Set<Entry<String, Object>> entrySet()
- {
- return getWrapped().entrySet();
- }
-
- @Override
- public void restoreState(FacesContext context, Object state)
- {
- _viewScopeId = (String) state;
- }
-
- @Override
- public Object saveState(FacesContext context)
- {
- return _viewScopeId;
- }
-
- @Override
- public boolean isTransient()
- {
- return false;
- }
-
- @Override
- public void setTransient(boolean newTransientValue)
- {
- }
-
- private static class ViewScope extends HashMap<String, Object>
- {
-
- private static final long serialVersionUID = -1088293802269478164L;
-
- @Override
- public void clear()
- {
- /*
- * The returned Map must be implemented such that calling clear() on the Map causes
- * Application.publishEvent(java.lang.Class, java.lang.Object) to be called, passing
- * ViewMapDestroyedEvent.class as the first argument and this UIViewRoot instance as the second argument.
- */
- FacesContext facesContext = FacesContext.getCurrentInstance();
- facesContext.getApplication().publishEvent(facesContext,
- PreDestroyViewMapEvent.class, facesContext.getViewRoot());
-
- super.clear();
- }
-
- }
-
-}
+/*
+ * 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.view;
+
+import jakarta.enterprise.inject.spi.BeanManager;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import jakarta.faces.component.StateHolder;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.event.PreDestroyViewMapEvent;
+import org.apache.myfaces.cdi.util.CDIUtils;
+import org.apache.myfaces.cdi.view.ViewScopeContextualStorageHolder;
+import org.apache.myfaces.cdi.view.ViewScopeCDIMap;
+import org.apache.myfaces.util.ExternalSpecifications;
+
+/**
+ * This wrapper has these objectives:
+ *
+ * - Isolate the part that needs to be saved with the view (viewScopeId) from
+ * the part that should remain into session (bean map). This class will be
+ * serialized when UIViewRoot.saveState() is called.
+ * - Decouple the way how the view scope map is stored. For example, in
+ * CDI view scope a session scope bean is used, and in default view scope
+ * the same session map is used but using a prefix.
+ *
+ * @author Leonardo Uribe
+ */
+public class ViewScopeProxyMap extends HashMap<String, Object> implements StateHolder
+{
+ private String _viewScopeId;
+
+ private transient Map<String, Object> _delegate;
+
+ public ViewScopeProxyMap()
+ {
+ }
+
+
+ public String getViewScopeId()
+ {
+ return _viewScopeId;
+ }
+
+ public void forceCreateWrappedMap(FacesContext facesContext)
+ {
+ getWrapped();
+ }
+
+ public Map<String, Object> getWrapped()
+ {
+ if (_delegate == null)
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+
+ if (facesContext != null)
+ {
+ // for unittests without CDI
+ if (!ExternalSpecifications.isCDIAvailable(facesContext.getExternalContext()))
+ {
+ _delegate = new ViewScope();
+ return _delegate;
+ }
+
+ if (_viewScopeId == null)
+ {
+ BeanManager beanManager = CDIUtils.getBeanManager(facesContext.getExternalContext());
+ ViewScopeContextualStorageHolder beanHolder =
+ CDIUtils.get(beanManager, ViewScopeContextualStorageHolder.class);
+ _viewScopeId = beanHolder.generateUniqueViewScopeId();
+ }
+ _delegate = new ViewScopeCDIMap(facesContext, _viewScopeId);
+ }
+ else
+ {
+ // In junit test cases, where there is no facesContext instance, it is enough to
+ // just get a blank instance.
+ _delegate = new ViewScope();
+ }
+ }
+ return _delegate;
+ }
+
+ @Override
+ public int size()
+ {
+ return getWrapped().size();
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return getWrapped().isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key)
+ {
+ return getWrapped().containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value)
+ {
+ return getWrapped().containsValue(value);
+ }
+
+ @Override
+ public Object get(Object key)
+ {
+ return getWrapped().get(key);
+ }
+
+ @Override
+ public Object put(String key, Object value)
+ {
+ return getWrapped().put(key, value);
+ }
+
+ @Override
+ public Object remove(Object key)
+ {
+ return getWrapped().remove(key);
+ }
+
+ @Override
+ public void putAll(Map<? extends String, ? extends Object> m)
+ {
+ getWrapped().putAll(m);
+ }
+
+ @Override
+ public void clear()
+ {
+ /*
+ * The returned Map must be implemented such that calling clear() on the Map causes
+ * Application.publishEvent(java.lang.Class, java.lang.Object) to be called, passing
+ * ViewMapDestroyedEvent.class as the first argument and this UIViewRoot instance as the second argument.
+ */
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ facesContext.getApplication().publishEvent(facesContext,
+ PreDestroyViewMapEvent.class, facesContext.getViewRoot());
+
+ getWrapped().clear();
+ }
+
+ @Override
+ public Set<String> keySet()
+ {
+ return getWrapped().keySet();
+ }
+
+ @Override
+ public Collection<Object> values()
+ {
+ return getWrapped().values();
+ }
+
+ @Override
+ public Set<Entry<String, Object>> entrySet()
+ {
+ return getWrapped().entrySet();
+ }
+
+ @Override
+ public void restoreState(FacesContext context, Object state)
+ {
+ _viewScopeId = (String) state;
+ }
+
+ @Override
+ public Object saveState(FacesContext context)
+ {
+ return _viewScopeId;
+ }
+
+ @Override
+ public boolean isTransient()
+ {
+ return false;
+ }
+
+ @Override
+ public void setTransient(boolean newTransientValue)
+ {
+ }
+
+ private static class ViewScope extends HashMap<String, Object>
+ {
+
+ private static final long serialVersionUID = -1088293802269478164L;
+
+ @Override
+ public void clear()
+ {
+ /*
+ * The returned Map must be implemented such that calling clear() on the Map causes
+ * Application.publishEvent(java.lang.Class, java.lang.Object) to be called, passing
+ * ViewMapDestroyedEvent.class as the first argument and this UIViewRoot instance as the second argument.
+ */
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ facesContext.getApplication().publishEvent(facesContext,
+ PreDestroyViewMapEvent.class, facesContext.getViewRoot());
+
+ super.clear();
+ }
+
+ }
+
+}
diff --git a/impl/src/main/java/org/apache/myfaces/webapp/MyFacesHttpSessionListener.java b/impl/src/main/java/org/apache/myfaces/webapp/MyFacesHttpSessionListener.java
index e1b7eba..2ece7d6 100644
--- a/impl/src/main/java/org/apache/myfaces/webapp/MyFacesHttpSessionListener.java
+++ b/impl/src/main/java/org/apache/myfaces/webapp/MyFacesHttpSessionListener.java
@@ -1,97 +1,97 @@
-/*
- * 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.webapp;
-
-import jakarta.faces.context.ExceptionHandler;
-import jakarta.faces.context.ExternalContext;
-import jakarta.faces.context.FacesContext;
-import jakarta.servlet.ServletContext;
-import jakarta.servlet.http.HttpSessionEvent;
-import jakarta.servlet.http.HttpSessionListener;
-import org.apache.myfaces.cdi.clientwindow.ClientWindowScopeContext;
-import org.apache.myfaces.cdi.view.ViewScopeContext;
-import org.apache.myfaces.context.ExceptionHandlerImpl;
-import org.apache.myfaces.context.servlet.StartupFacesContextImpl;
-import org.apache.myfaces.context.servlet.StartupServletExternalContextImpl;
-import org.apache.myfaces.spi.FacesFlowProvider;
-
-public class MyFacesHttpSessionListener implements HttpSessionListener
-{
- public static final String APPLICATION_MAP_KEY = MyFacesHttpSessionListener.class.getName();
-
- private FacesFlowProvider facesFlowProvider = null;
-
- public void setFacesFlowProvider(FacesFlowProvider facesFlowProvider)
- {
- this.facesFlowProvider = facesFlowProvider;
- }
-
- @Override
- public void sessionCreated(HttpSessionEvent event)
- {
- }
-
- @Override
- public void sessionDestroyed(HttpSessionEvent event)
- {
- // If we don't propagate this event, CDI will do for us but outside JSF control
- // so when @PreDestroy methods are called there will not be an active FacesContext.
- // The trick here is ensure clean the affected scopes to avoid duplicates.
- // Remember cdi session scope is different from jsf session scope, because in
- // jsf case the beans are stored under a session attribute, so it has the problem
- // with attributeRemoved, but on cdi a wrapper is used instead, avoiding the problem.
- FacesContext facesContext = FacesContext.getCurrentInstance();
- if (facesContext != null)
- {
- if (facesFlowProvider != null)
- {
- facesFlowProvider.onSessionDestroyed();
- }
- ViewScopeContext.onSessionDestroyed(facesContext);
- ClientWindowScopeContext.onSessionDestroyed(facesContext);
- }
- else
- {
- // In case no FacesContext is available, we are on session invalidation
- // through timeout. In that case, create a dummy FacesContext for this one
- // like the one used in startup or shutdown and invoke the destroy method.
- try
- {
- ServletContext servletContext = event.getSession().getServletContext();
- ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, false);
- ExceptionHandler exceptionHandler = new ExceptionHandlerImpl();
- facesContext = new StartupFacesContextImpl(externalContext, externalContext, exceptionHandler, false);
-
- if (facesFlowProvider != null)
- {
- facesFlowProvider.onSessionDestroyed();
- }
- ViewScopeContext.onSessionDestroyed(facesContext);
- ClientWindowScopeContext.onSessionDestroyed(facesContext);
- }
- finally
- {
- if (facesContext != null)
- {
- facesContext.release();
- }
- }
- }
- }
-}
+/*
+ * 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.webapp;
+
+import jakarta.faces.context.ExceptionHandler;
+import jakarta.faces.context.ExternalContext;
+import jakarta.faces.context.FacesContext;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.http.HttpSessionEvent;
+import jakarta.servlet.http.HttpSessionListener;
+import org.apache.myfaces.cdi.clientwindow.ClientWindowScopeContext;
+import org.apache.myfaces.cdi.view.ViewScopeContext;
+import org.apache.myfaces.context.ExceptionHandlerImpl;
+import org.apache.myfaces.context.servlet.StartupFacesContextImpl;
+import org.apache.myfaces.context.servlet.StartupServletExternalContextImpl;
+import org.apache.myfaces.spi.FacesFlowProvider;
+
+public class MyFacesHttpSessionListener implements HttpSessionListener
+{
+ public static final String APPLICATION_MAP_KEY = MyFacesHttpSessionListener.class.getName();
+
+ private FacesFlowProvider facesFlowProvider = null;
+
+ public void setFacesFlowProvider(FacesFlowProvider facesFlowProvider)
+ {
+ this.facesFlowProvider = facesFlowProvider;
+ }
+
+ @Override
+ public void sessionCreated(HttpSessionEvent event)
+ {
+ }
+
+ @Override
+ public void sessionDestroyed(HttpSessionEvent event)
+ {
+ // If we don't propagate this event, CDI will do for us but outside JSF control
+ // so when @PreDestroy methods are called there will not be an active FacesContext.
+ // The trick here is ensure clean the affected scopes to avoid duplicates.
+ // Remember cdi session scope is different from jsf session scope, because in
+ // jsf case the beans are stored under a session attribute, so it has the problem
+ // with attributeRemoved, but on cdi a wrapper is used instead, avoiding the problem.
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ if (facesContext != null)
+ {
+ if (facesFlowProvider != null)
+ {
+ facesFlowProvider.onSessionDestroyed();
+ }
+ ViewScopeContext.destroyAll(facesContext);
+ ClientWindowScopeContext.destroyAll(facesContext);
+ }
+ else
+ {
+ // In case no FacesContext is available, we are on session invalidation
+ // through timeout. In that case, create a dummy FacesContext for this one
+ // like the one used in startup or shutdown and invoke the destroy method.
+ try
+ {
+ ServletContext servletContext = event.getSession().getServletContext();
+ ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, false);
+ ExceptionHandler exceptionHandler = new ExceptionHandlerImpl();
+ facesContext = new StartupFacesContextImpl(externalContext, externalContext, exceptionHandler, false);
+
+ if (facesFlowProvider != null)
+ {
+ facesFlowProvider.onSessionDestroyed();
+ }
+ ViewScopeContext.destroyAll(facesContext);
+ ClientWindowScopeContext.destroyAll(facesContext);
+ }
+ finally
+ {
+ if (facesContext != null)
+ {
+ facesContext.release();
+ }
+ }
+ }
+ }
+}