You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by st...@apache.org on 2010/06/29 01:07:23 UTC
svn commit: r958778 - in
/myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main:
java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/
java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/
resources/META-INF/services/
Author: struberg
Date: Mon Jun 28 23:07:23 2010
New Revision: 958778
URL: http://svn.apache.org/viewvc?rev=958778&view=rev
Log:
EXTCDI-14 CDI support for @ViewScoped
Added:
myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/
myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/
myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/MockViewScopedContext.java
myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/ViewScopedContext.java
myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/ViewScopedExtension.java
Modified:
myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
Added: myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/MockViewScopedContext.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/MockViewScopedContext.java?rev=958778&view=auto
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/MockViewScopedContext.java (added)
+++ myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/MockViewScopedContext.java Mon Jun 28 23:07:23 2010
@@ -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.extensions.cdi.javaee.jsf.impl.scope.view;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+/**
+ * <p>This is a Mock version of the {@link ViewScopedContext}.
+ * It will automatically get used instead of it's parent class
+ * if we are in the
+ * {@link javax.faces.application.ProjectStage#UnitTest}.</p>
+ *
+ * <p>There is of course no automatic cleaning if we transit over to the
+ * next view! In this case you can use {@link #resetViewMap()} to clean
+ * the mock ViewMap manually in your unit test.</p>
+ *
+ * <p><b>Attention:</b> The ViewMap is a shared static ConcurrentHashMap,
+ * so this implementation is not able to emulate the behaviour of multiple views
+ * at the same time!</p>
+ */
+public class MockViewScopedContext extends ViewScopedContext
+{
+
+ private static Map<String, Object> mockViewMap = new ConcurrentHashMap<String, Object>();
+
+ /**
+ * Simply clear the mock ViewMap.
+ * This function should get called if the same unit test needs to test multiple
+ * views and also between different tests if the container doesn't get restarted
+ * in between.
+ */
+ public static void resetViewMap()
+ {
+ mockViewMap.clear();
+ }
+
+ @Override
+ protected Map<String, Object> getViewMap()
+ {
+ return mockViewMap;
+ }
+
+ /**
+ * @return always <code>true</code> since in a unit test it's always active.
+ */
+ @Override
+ public boolean isActive()
+ {
+ return true;
+ }
+}
\ No newline at end of file
Added: myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/ViewScopedContext.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/ViewScopedContext.java?rev=958778&view=auto
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/ViewScopedContext.java (added)
+++ myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/ViewScopedContext.java Mon Jun 28 23:07:23 2010
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.extensions.cdi.javaee.jsf.impl.scope.view;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.enterprise.context.ContextNotActiveException;
+import javax.enterprise.context.spi.Context;
+import javax.enterprise.context.spi.Contextual;
+import javax.enterprise.context.spi.CreationalContext;
+
+import javax.faces.bean.ViewScoped;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+import javax.faces.event.PreDestroyViewMapEvent;
+import javax.faces.event.SystemEvent;
+import javax.faces.event.SystemEventListener;
+
+/**
+ * This class provides the contexts lifecycle for the
+ * new JSF-2 @ViewScoped Context
+ */
+public class ViewScopedContext implements Context, SystemEventListener
+{
+
+ private final static String COMPONENT_MAP_NAME ="codi.componentInstanceMap";
+ private final static String CREATIONAL_MAP_NAME ="codi.creationalInstanceMap";
+
+ private boolean isJsfSubscribed = false;
+
+
+ public <T> T get(Contextual<T> component)
+ {
+ checkActive();
+
+ if(!isJsfSubscribed)
+ {
+ FacesContext.getCurrentInstance().getApplication().subscribeToEvent(PreDestroyViewMapEvent.class, this);
+
+ isJsfSubscribed = true;
+ }
+
+ Map<String, Object> viewMap = getViewMap();
+
+ @SuppressWarnings("unchecked")
+ ConcurrentHashMap<Contextual<?>, Object> componentInstanceMap
+ = (ConcurrentHashMap<Contextual<?>, Object>) viewMap.get(COMPONENT_MAP_NAME);
+
+ if(componentInstanceMap == null)
+ {
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ T instance = (T) componentInstanceMap.get(component);
+
+ return instance;
+ }
+
+ public <T> T get(Contextual<T> component, CreationalContext<T> creationalContext)
+ {
+ checkActive();
+
+ Map<String, Object> viewMap = getViewMap();
+
+ @SuppressWarnings("unchecked")
+ ConcurrentHashMap<Contextual<?>, Object> componentInstanceMap
+ = (ConcurrentHashMap<Contextual<?>, Object>) viewMap.get(COMPONENT_MAP_NAME);
+
+ if(componentInstanceMap == null)
+ {
+ // TODO we now need to start being carefull with reentrancy...
+ componentInstanceMap = new ConcurrentHashMap<Contextual<?>, Object>();
+ viewMap.put(COMPONENT_MAP_NAME, componentInstanceMap);
+ }
+
+ @SuppressWarnings("unchecked")
+ ConcurrentHashMap<Contextual<?>, CreationalContext<?>> creationalContextMap
+ = (ConcurrentHashMap<Contextual<?>, CreationalContext<?>>) viewMap.get(CREATIONAL_MAP_NAME);
+ if(creationalContextMap == null)
+ {
+ // TODO we now need to start being carefull with reentrancy...
+ creationalContextMap = new ConcurrentHashMap<Contextual<?>, CreationalContext<?>>();
+ viewMap.put(CREATIONAL_MAP_NAME, creationalContextMap);
+ }
+
+ @SuppressWarnings("unchecked")
+ T instance = (T) componentInstanceMap.get(component);
+ if (instance != null)
+ {
+ return instance;
+ }
+
+ if(creationalContext == null)
+ {
+ return null;
+ }
+
+ synchronized (componentInstanceMap)
+ {
+ // just to make sure...
+ @SuppressWarnings("unchecked")
+ T i = (T)componentInstanceMap.get(component);
+ if (i != null)
+ {
+ return i;
+ }
+
+ instance = component.create(creationalContext);
+
+ if (instance != null)
+ {
+ componentInstanceMap.put(component, instance);
+ creationalContextMap.put(component, creationalContext);
+ }
+ }
+
+ return instance;
+ }
+
+ public Class<? extends Annotation> getScope()
+ {
+ return ViewScoped.class;
+ }
+
+ /**
+ * The view context is active if a valid ViewRoot could be detected.
+ */
+ public boolean isActive()
+ {
+ return getViewRoot() != null;
+ }
+
+ private void checkActive()
+ {
+ if (!isActive())
+ {
+ throw new ContextNotActiveException("WebBeans context with scope annotation " +
+ "@ViewScoped is not active with respect to the current thread");
+ }
+ }
+
+ public boolean isListenerForSource(Object source)
+ {
+ if (source instanceof UIViewRoot)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * We get PreDestroyViewMapEvent events from the JSF servlet and destroy our contextual
+ * instances. This should (theoretically!) also get fired if the webapp closes, so there
+ * should be no need to manually track all view scopes and destroy them at a shutdown.
+ *
+ * @see javax.faces.event.SystemEventListener#processEvent(javax.faces.event.SystemEvent)
+ */
+ @SuppressWarnings("unchecked")
+ public void processEvent(SystemEvent event)
+ {
+ if (event instanceof PreDestroyViewMapEvent)
+ {
+ // better use the viewmap we get from the event to prevent concurrent modification problems
+ Map<String, Object> viewMap = ((UIViewRoot) event.getSource()).getViewMap();
+
+ ConcurrentHashMap<Contextual<?>, Object> componentInstanceMap
+ = (ConcurrentHashMap<Contextual<?>, Object>) viewMap.get(COMPONENT_MAP_NAME);
+
+ ConcurrentHashMap<Contextual<?>, CreationalContext<?>> creationalContextMap
+ = (ConcurrentHashMap<Contextual<?>, CreationalContext<?>>) viewMap.get(CREATIONAL_MAP_NAME);
+
+ if(componentInstanceMap != null)
+ {
+ for ( Entry<Contextual<?>, Object> componentEntry : componentInstanceMap.entrySet())
+ {
+ // there is no nice way to explain the Java Compiler that we are handling the same type T,
+ // therefore we need completely drop the type information :(
+ Contextual contextual = componentEntry.getKey();
+ Object instance = componentEntry.getValue();
+ CreationalContext creational = creationalContextMap.get(contextual);
+
+ contextual.destroy(instance, creational);
+ }
+ }
+ }
+ }
+
+
+ protected UIViewRoot getViewRoot()
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+
+ if(context != null)
+ {
+ return context.getViewRoot();
+ }
+
+ return null;
+ }
+
+ protected Map<String, Object> getViewMap()
+ {
+ UIViewRoot viewRoot = getViewRoot();
+
+ if (viewRoot != null)
+ {
+ return viewRoot.getViewMap(true);
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
Added: myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/ViewScopedExtension.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/ViewScopedExtension.java?rev=958778&view=auto
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/ViewScopedExtension.java (added)
+++ myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/javaee/jsf/impl/scope/view/ViewScopedExtension.java Mon Jun 28 23:07:23 2010
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.extensions.cdi.javaee.jsf.impl.scope.view;
+
+
+import org.apache.myfaces.extensions.cdi.core.api.projectstage.ProjectStage;
+import org.apache.myfaces.extensions.cdi.core.impl.projectstage.ProjectStageProducer;
+
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.AfterBeanDiscovery;
+import javax.enterprise.inject.spi.BeforeBeanDiscovery;
+import javax.enterprise.inject.spi.Extension;
+import javax.faces.bean.ViewScoped;
+
+
+/**
+ * <p>This CDI extension enables support for the new JSF-2 @ViewScoped annotation.</p>
+ *
+ * <p>2 steps are necessary:</p>
+ * <ol>
+ * <li>We have to manually add the @ViewScoped annotation
+ * as a passivating {@link javax.enterprise.context.NormalScope},
+ * since this annotation is neither a JSR-330 annotation
+ * nor a JSR-299 annotation and therefor doesn't get picked up automatically. This has to be
+ * before the bean scanning starts.</li>
+ * <li>After the bean scanning succeeded, we register the
+ * {@link javax.enterprise.context.spi.Context} for the
+ * ViewScoped. The {@link ViewScopedContext} is responsible for actually storing all our
+ * @ViewScoped contextual instances in the JSF ViewMap.</li>
+ * </ol>
+ *
+ * <p>The extension automatically detects if we are in the
+ * {@link ProjectStage#UnitTest} and uses
+ * </p>
+ */
+public class ViewScopedExtension implements Extension
+{
+
+ public void addViewScoped(@Observes BeforeBeanDiscovery beforeBeanDiscovery)
+ {
+ beforeBeanDiscovery.addScope(ViewScoped.class, true, true);
+ }
+
+ public void registerViewContext(@Observes AfterBeanDiscovery afterBeanDiscovery)
+ {
+ // we need to do this manually because there is no dependency injection in place
+ // at this time
+ ProjectStageProducer psp = null;
+ try
+ {
+ psp = ProjectStageProducer.getInstance();
+ }
+ catch (Exception e)
+ {
+ if (!(e instanceof RuntimeException))
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ psp.determineProjectStage();
+ ProjectStage projectStage = psp.getProjectStage();
+
+ if (projectStage == ProjectStage.UnitTest)
+ {
+ // for unit tests, we use the mock context
+ afterBeanDiscovery.addContext(new MockViewScopedContext());
+ }
+ else
+ {
+ // otherwise we use the real JSF ViewMap context
+ afterBeanDiscovery.addContext(new ViewScopedContext());
+ }
+ }
+
+}
\ No newline at end of file
Modified: myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension?rev=958778&r1=958777&r2=958778&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jsf20-module/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension Mon Jun 28 23:07:23 2010
@@ -15,4 +15,7 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-#####################################################################################
\ No newline at end of file
+#####################################################################################
+
+# myfaces-codi support for @ViewScoped
+#org.apache.myfaces.extensions.cdi.javaee.jsf.impl.scope.view.ViewScopedExtension