You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2013/08/12 01:54:53 UTC

svn commit: r1513021 [3/3] - in /myfaces/core/trunk: api/src/main/java/javax/faces/component/ impl/src/main/java/org/apache/myfaces/cdi/ impl/src/main/java/org/apache/myfaces/cdi/impl/ impl/src/main/java/org/apache/myfaces/cdi/util/ impl/src/main/java/...

Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/impl/DefaultViewScopeProviderFactory.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/impl/DefaultViewScopeProviderFactory.java?rev=1513021&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/impl/DefaultViewScopeProviderFactory.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/impl/DefaultViewScopeProviderFactory.java Sun Aug 11 23:54:52 2013
@@ -0,0 +1,69 @@
+/*
+ * 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.spi.impl;
+
+import javax.faces.context.ExternalContext;
+import org.apache.myfaces.shared.util.ClassUtils;
+import org.apache.myfaces.spi.ViewScopeProvider;
+import org.apache.myfaces.spi.ViewScopeProviderFactory;
+import org.apache.myfaces.util.ExternalSpecifications;
+import org.apache.myfaces.view.impl.DefaultViewScopeHandler;
+
+/**
+ *
+ * @author Leonardo Uribe
+ */
+public class DefaultViewScopeProviderFactory extends ViewScopeProviderFactory
+{
+    
+    public static final String VIEW_SCOPE_HANDLER = ViewScopeProvider.class.getName();
+    public static final String VIEW_SCOPE_HANDLER_INSTANCE_KEY = VIEW_SCOPE_HANDLER + ".INSTANCE";
+
+    @Override
+    public ViewScopeProvider getViewScopeHandler(ExternalContext externalContext)
+    {
+        // check for cached instance
+        ViewScopeProvider returnValue = (ViewScopeProvider)
+                externalContext.getApplicationMap().get(VIEW_SCOPE_HANDLER_INSTANCE_KEY);
+
+        if (returnValue == null)
+        {
+            if (ExternalSpecifications.isCDIAvailable(externalContext))
+            {
+                returnValue = (ViewScopeProvider) ClassUtils.newInstance(
+                    "org.apache.myfaces.cdi.impl.CDIManagedBeanHandlerImpl");
+                    //CDIManagedBeanHandler.getInstance(externalContext);
+            }
+            else
+            {
+                returnValue = new DefaultViewScopeHandler();
+            }
+            // cache the result on the ApplicationMap
+            externalContext.getApplicationMap().put(VIEW_SCOPE_HANDLER_INSTANCE_KEY, returnValue);
+        }
+
+        return returnValue;
+    }
+
+    @Override
+    public void setViewScopeHandler(ExternalContext externalContext, ViewScopeProvider viewScopeHandler)
+    {
+        externalContext.getApplicationMap().put(VIEW_SCOPE_HANDLER_INSTANCE_KEY, viewScopeHandler);
+    }
+}

Propchange: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/spi/impl/DefaultViewScopeProviderFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/util/ExternalSpecifications.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/util/ExternalSpecifications.java?rev=1513021&r1=1513020&r2=1513021&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/util/ExternalSpecifications.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/util/ExternalSpecifications.java Sun Aug 11 23:54:52 2013
@@ -22,6 +22,8 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import javax.el.ELContext;
+import javax.faces.context.ExternalContext;
+import org.apache.myfaces.webapp.AbstractFacesInitializer;
 
 /**
  * <p>
@@ -42,6 +44,7 @@ public final class ExternalSpecification
 
     private static volatile Boolean beanValidationAvailable;
     private static volatile Boolean unifiedELAvailable;
+    private static volatile Boolean cdiAvailable;
 
     /**
      * This method determines if Bean Validation is present.
@@ -124,6 +127,41 @@ public final class ExternalSpecification
         }
         return unifiedELAvailable;
     }
+    
+    public static boolean isCDIAvailable(ExternalContext externalContext)
+    {
+        if (cdiAvailable == null)
+        {
+            try
+            {
+                cdiAvailable = Class.forName("javax.enterprise.inject.spi.BeanManager") != null;
+            }
+            catch (Throwable t)
+            {
+                //log.log(Level.FINE, "Error loading class (could be normal)", t);
+                cdiAvailable = false;
+            }
+            
+            if (cdiAvailable)
+            {
+                cdiAvailable = externalContext.getApplicationMap().containsKey(
+                    AbstractFacesInitializer.CDI_BEAN_MANAGER_INSTANCE);
+            }
+
+            log.info("MyFaces CDI support " + (cdiAvailable ? "enabled" : "disabled"));
+            
+            return cdiAvailable;
+        }
+        else
+        {
+            if (Boolean.TRUE.equals(cdiAvailable))
+            {
+                return externalContext.getApplicationMap().containsKey(
+                        AbstractFacesInitializer.CDI_BEAN_MANAGER_INSTANCE);
+            }
+            return cdiAvailable;
+        }
+    }
 
     /**
      * this class should not be instantiated.

Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/ViewScopeProxyMap.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/ViewScopeProxyMap.java?rev=1513021&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/ViewScopeProxyMap.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/ViewScopeProxyMap.java Sun Aug 11 23:54:52 2013
@@ -0,0 +1,175 @@
+/*
+ * 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 java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import javax.faces.component.StateHolder;
+import javax.faces.context.FacesContext;
+import javax.faces.event.PreDestroyViewMapEvent;
+import org.apache.myfaces.spi.ViewScopeProvider;
+import org.apache.myfaces.spi.ViewScopeProviderFactory;
+
+/**
+ * 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 implements Map<String, Object>, 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();
+            
+            ViewScopeProviderFactory factory = ViewScopeProviderFactory.getViewScopeHandlerFactory(
+                facesContext.getExternalContext());
+            
+            ViewScopeProvider handler = factory.getViewScopeHandler(facesContext.getExternalContext());
+            
+            if (_viewScopeId == null)
+            {
+                _viewScopeId = handler.generateViewScopeId(facesContext);
+                _delegate = handler.createViewScopeMap(facesContext, _viewScopeId);
+            }
+            else
+            {
+                _delegate = handler.restoreViewScopeMap(facesContext, _viewScopeId);
+            }
+        }
+        return _delegate;
+    }
+    
+    public int size()
+    {
+        return getWrapped().size();
+    }
+
+    public boolean isEmpty()
+    {
+        return getWrapped().isEmpty();
+    }
+
+    public boolean containsKey(Object key)
+    {
+        return getWrapped().containsKey(key);
+    }
+
+    public boolean containsValue(Object value)
+    {
+        return getWrapped().containsValue(value);
+    }
+
+    public Object get(Object key)
+    {
+        return getWrapped().get(key);
+    }
+
+    public Object put(String key, Object value)
+    {
+        return getWrapped().put(key, value);
+    }
+
+    public Object remove(Object key)
+    {
+        return getWrapped().remove(key);
+    }
+
+    public void putAll(Map<? extends String, ? extends Object> m)
+    {
+        getWrapped().putAll(m);
+    }
+
+    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();
+    }
+
+    public Set<String> keySet()
+    {
+        return getWrapped().keySet();
+    }
+
+    public Collection<Object> values()
+    {
+        return getWrapped().values();
+    }
+
+    public Set<Entry<String, Object>> entrySet()
+    {
+        return getWrapped().entrySet();
+    }
+
+    public void restoreState(FacesContext context, Object state)
+    {
+        _viewScopeId = (String) state;
+    }
+
+    public Object saveState(FacesContext context)
+    {
+        return _viewScopeId;
+    }
+
+    public boolean isTransient()
+    {
+        return false;
+    }
+
+    public void setTransient(boolean newTransientValue)
+    {
+    }
+
+}

Propchange: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/ViewScopeProxyMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/impl/DefaultViewScopeHandler.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/impl/DefaultViewScopeHandler.java?rev=1513021&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/impl/DefaultViewScopeHandler.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/impl/DefaultViewScopeHandler.java Sun Aug 11 23:54:52 2013
@@ -0,0 +1,147 @@
+/*
+ * 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.impl;
+
+import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import org.apache.myfaces.spi.ViewScopeProvider;
+
+/**
+ * Minimal implementation for view scope without CDI but always store
+ * the beans into session.
+ * 
+ * @author Leonardo Uribe
+ */
+public class DefaultViewScopeHandler extends ViewScopeProvider
+{
+    private static final String VIEW_SCOPE_PREFIX = "oam.view.SCOPE";
+    
+    private static final String VIEW_SCOPE_PREFIX_KEY = VIEW_SCOPE_PREFIX+".KEY";
+    
+    private static final String VIEW_SCOPE_PREFIX_MAP = VIEW_SCOPE_PREFIX+".MAP";
+    
+    static final char SEPARATOR_CHAR = '.';
+    
+    private final AtomicLong _count;
+    
+    public DefaultViewScopeHandler()
+    {
+        _count = new AtomicLong(_getSeed());
+    }
+    
+    /**
+     * Returns a cryptographically secure random number to use as the _count seed
+     */
+    private static long _getSeed()
+    {
+        SecureRandom rng;
+        try
+        {
+            // try SHA1 first
+            rng = SecureRandom.getInstance("SHA1PRNG");
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            // SHA1 not present, so try the default (which could potentially not be
+            // cryptographically secure)
+            rng = new SecureRandom();
+        }
+
+        // use 48 bits for strength and fill them in
+        byte[] randomBytes = new byte[6];
+        rng.nextBytes(randomBytes);
+
+        // convert to a long
+        return new BigInteger(randomBytes).longValue();
+    }
+    
+    /**
+     * Get the next token to be assigned to this request
+     * 
+     * @return
+     */
+    private String _getNextToken()
+    {
+        // atomically increment the value
+        long nextToken = _count.incrementAndGet();
+
+        // convert using base 36 because it is a fast efficient subset of base-64
+        return Long.toString(nextToken, 36);
+    }
+    
+    public void onSessionDestroyed()
+    {
+        // TODO: Implement @PreDestroy? In JSF 2.0 this part was not present, 
+        // but in theory we should do it here.
+    }
+    
+    public Map<String, Object> createViewScopeMap(FacesContext facesContext, String viewScopeId)
+    {
+        String fullToken = VIEW_SCOPE_PREFIX_MAP + SEPARATOR_CHAR + viewScopeId;
+        Map<String, Object> map = _createSubKeyMap(facesContext, fullToken);
+        return map;
+    }
+    
+    public Map<String, Object> restoreViewScopeMap(FacesContext facesContext, String viewScopeId)
+    {
+        String fullToken = VIEW_SCOPE_PREFIX_MAP + SEPARATOR_CHAR + viewScopeId;
+        Map<String, Object> map = _createSubKeyMap(facesContext, fullToken);
+        return map;
+    }
+    
+    private Map<String, Object> _createSubKeyMap(FacesContext context, String prefix)
+    {
+        ExternalContext external = context.getExternalContext();
+        Map<String, Object> sessionMap = external.getSessionMap();
+
+        return new SubKeyMap<Object>(sessionMap, prefix);
+    }
+    
+    public String generateViewScopeId(FacesContext facesContext)
+    {
+        // To ensure uniqueness in this part we use a counter that 
+        // is stored into session and we add a random number to
+        // make difficult to guess the next number.
+        ExternalContext externalContext = facesContext.getExternalContext();
+        Object sessionObj = externalContext.getSession(true);
+        Integer sequence = null;
+        // synchronized to increase sequence if multiple requests
+        // are handled at the same time for the session
+        synchronized (sessionObj) 
+        {
+            Map<String, Object> map = externalContext.getSessionMap();
+            sequence = (Integer) map.get(VIEW_SCOPE_PREFIX_KEY);
+            if (sequence == null || sequence.intValue() == Integer.MAX_VALUE)
+            {
+                sequence = Integer.valueOf(1);
+            }
+            else
+            {
+                sequence = Integer.valueOf(sequence.intValue());
+            }
+            map.put(VIEW_SCOPE_PREFIX_KEY, sequence);
+        }
+        return _getNextToken()+'_'+sequence.toString();
+    }
+}

Propchange: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/impl/DefaultViewScopeHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Copied: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/impl/SubKeyMap.java (from r1509781, myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/cdi/SubKeyMap.java)
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/impl/SubKeyMap.java?p2=myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/impl/SubKeyMap.java&p1=myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/cdi/SubKeyMap.java&r1=1509781&r2=1513021&rev=1513021&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/cdi/SubKeyMap.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/impl/SubKeyMap.java Sun Aug 11 23:54:52 2013
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.myfaces.flow.cdi;
+package org.apache.myfaces.view.impl;
 
 import java.util.AbstractMap;
 import java.util.AbstractSet;

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/AbstractFacesInitializer.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/AbstractFacesInitializer.java?rev=1513021&r1=1513020&r2=1513021&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/AbstractFacesInitializer.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/AbstractFacesInitializer.java Sun Aug 11 23:54:52 2013
@@ -53,7 +53,11 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
 import org.apache.myfaces.util.ExternalSpecifications;
+import org.apache.myfaces.spi.ViewScopeProvider;
+import org.apache.myfaces.spi.ViewScopeProviderFactory;
 
 /**
  * Performs common initialization tasks.
@@ -94,6 +98,11 @@ public abstract class AbstractFacesIniti
     @JSFWebConfigParam(expectedValues="true, auto, false", defaultValue="auto")
     public static final String INIT_PARAM_LOG_WEB_CONTEXT_PARAMS = "org.apache.myfaces.LOG_WEB_CONTEXT_PARAMS";
     public static final String INIT_PARAM_LOG_WEB_CONTEXT_PARAMS_DEFAULT ="auto";
+    
+    public static final String CDI_BEAN_MANAGER_INSTANCE = "oam.cdi.BEAN_MANAGER_INSTANCE";
+    
+    private static final String CDI_SERVLET_CONTEXT_BEAN_MANAGER_ATTRIBUTE = 
+        "javax.enterprise.inject.spi.BeanManager";
 
     /**
      * Performs all necessary initialization tasks like configuring this JSF
@@ -141,6 +150,20 @@ public abstract class AbstractFacesIniti
             }
 
             initContainerIntegration(servletContext, externalContext);
+            
+            initCDIIntegration(servletContext, externalContext);
+            
+            ViewScopeProviderFactory factory = ViewScopeProviderFactory.getViewScopeHandlerFactory(
+                externalContext);
+            
+            ViewScopeProvider viewScopeHandler = factory.getViewScopeHandler(
+                externalContext);
+            
+            ManagedBeanDestroyerListener listener = (ManagedBeanDestroyerListener)
+                externalContext.getApplicationMap().get(
+                    ManagedBeanDestroyerListener.APPLICATION_MAP_KEY);
+            
+            listener.setViewScopeHandler(viewScopeHandler);
 
             String useEncryption = servletContext.getInitParameter(StateUtils.USE_ENCRYPTION);
             if (!"false".equals(useEncryption)) // the default value is true
@@ -499,4 +522,56 @@ public abstract class AbstractFacesIniti
     protected abstract void initContainerIntegration(
             ServletContext servletContext, ExternalContext externalContext);
 
+    /**
+     * The intention of this method is provide a point where CDI integration is done.
+     * Faces Flow and javax.faces.view.ViewScope requires CDI in order to work, so
+     * this method should set a BeanManager instance on application map under
+     * the key "oam.cdi.BEAN_MANAGER_INSTANCE". The default implementation look on
+     * ServletContext first and then use JNDI.
+     * 
+     * @param servletContext
+     * @param externalContext 
+     */
+    protected void initCDIIntegration(
+            ServletContext servletContext, ExternalContext externalContext)
+    {
+        // Lookup bean manager and put it into an application scope attribute to 
+        // access it later. Remember the trick here is do not call any CDI api 
+        // directly, so if no CDI api is on the classpath no exception will be thrown.
+        
+        // Try with servlet context
+        Object beanManager = servletContext.getAttribute(
+            CDI_SERVLET_CONTEXT_BEAN_MANAGER_ATTRIBUTE);
+        if (beanManager == null)
+        {
+            // Try with JNDI
+            try
+            {
+                // in an application server
+                beanManager = InitialContext.doLookup("java:comp/BeanManager");
+            }
+            catch (NamingException e)
+            {
+                // silently ignore
+            }
+
+            if (beanManager == null)
+            {
+                try
+                {
+                    // in a servlet container
+                    beanManager = InitialContext.doLookup("java:comp/env/BeanManager");
+                }
+                catch (NamingException e)
+                {
+                    // silently ignore
+                }
+            }
+        }
+        if (beanManager != null)
+        {
+            externalContext.getApplicationMap().put(CDI_BEAN_MANAGER_INSTANCE, 
+                beanManager);
+        }
+    }
 }

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/ManagedBeanDestroyerListener.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/ManagedBeanDestroyerListener.java?rev=1513021&r1=1513020&r2=1513021&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/ManagedBeanDestroyerListener.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/ManagedBeanDestroyerListener.java Sun Aug 11 23:54:52 2013
@@ -36,6 +36,7 @@ import javax.servlet.http.HttpSessionEve
 import javax.servlet.http.HttpSessionListener;
 
 import org.apache.myfaces.config.ManagedBeanDestroyer;
+import org.apache.myfaces.spi.ViewScopeProvider;
 
 /**
  * Listens to
@@ -63,6 +64,8 @@ public class ManagedBeanDestroyerListene
     public static final String APPLICATION_MAP_KEY = "org.apache.myfaces.ManagedBeanDestroyerListener";
 
     private ManagedBeanDestroyer _destroyer = null;
+    
+    private ViewScopeProvider _viewScopeHandler = null;
 
     /**
      * Sets the ManagedBeanDestroyer instance to use.
@@ -73,6 +76,11 @@ public class ManagedBeanDestroyerListene
     {
         _destroyer = destroyer;
     }
+    
+    public void setViewScopeHandler(ViewScopeProvider listener)
+    {
+        _viewScopeHandler = listener;
+    }
 
     /* Session related methods ***********************************************/
     
@@ -128,6 +136,17 @@ public class ManagedBeanDestroyerListene
                 _destroyer.destroy(name, value);
             }
         }*/
+        
+        // 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.
+        if (_viewScopeHandler != null)
+        {
+            _viewScopeHandler.onSessionDestroyed();
+        }
     }
     
     /* Context related methods ***********************************************/

Modified: myfaces/core/trunk/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension?rev=1513021&r1=1513020&r2=1513021&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension (original)
+++ myfaces/core/trunk/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension Sun Aug 11 23:54:52 2013
@@ -1,2 +1,3 @@
+org.apache.myfaces.cdi.view.ViewScopeContextExtension
 org.apache.myfaces.flow.cdi.FlowBuilderCDIExtension
 org.apache.myfaces.flow.cdi.FlowScopeCDIExtension

Modified: myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java?rev=1513021&r1=1513020&r2=1513021&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java (original)
+++ myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java Sun Aug 11 23:54:52 2013
@@ -119,6 +119,8 @@ public class FlowMyFacesRequestTestCase 
         Assert.assertNotNull(currentFlow);
         Assert.assertEquals("flow1", currentFlow.getId());
         
+        facesContext.getApplication().getFlowHandler().getCurrentFlowScope().put("flow1","value1");
+        
         processRender();
         
         UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:call_flow2");
@@ -129,6 +131,8 @@ public class FlowMyFacesRequestTestCase 
         currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
         Assert.assertNotNull(currentFlow);
         Assert.assertEquals("flow2", currentFlow.getId());
+        Assert.assertFalse(facesContext.getApplication().getFlowHandler().getCurrentFlowScope().containsKey("flow1"));
+        facesContext.getApplication().getFlowHandler().getCurrentFlowScope().put("flow2","value2");
         
         processRender();