You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ma...@apache.org on 2010/03/09 02:02:35 UTC

svn commit: r920601 - in /myfaces/trinidad/branches/1.2.12.1.2-branch: trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ trinidad-api/src/test/java/org/apache/myfaces/trinidad/util/ trinidad-impl/src/main/java/org/apache/myfaces/trinidadinte...

Author: matzew
Date: Tue Mar  9 01:02:35 2010
New Revision: 920601

URL: http://svn.apache.org/viewvc?rev=920601&view=rev
Log:
putting TRINIDAD-1676 and TRINIDAD-1739 to another branch

Added:
    myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ComponentReference.java
    myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-api/src/test/java/org/apache/myfaces/trinidad/util/ComponentReferenceTest.java
Modified:
    myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java

Added: myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ComponentReference.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ComponentReference.java?rev=920601&view=auto
==============================================================================
--- myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ComponentReference.java (added)
+++ myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ComponentReference.java Tue Mar  9 01:02:35 2010
@@ -0,0 +1,680 @@
+/*
+ *  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.trinidad.util;
+
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+
+/**
+ * A utility to store a reference to an <code>UIComponent</code>. Application developers
+ * should use this tool if they need to have a reference to an instance of the
+ * <code>UIComponent</code> class in <code>managed beans</code> that are longer than <b>requested scoped</b>
+ * --for example Session and Application Scoped.  The reference will return the UIComponent, if any, with
+ * the same scoped id as the Component used to create the reference, in the current UIViewRoot.
+ * 
+ * Use <code>newUIComponentReference()</code> to create a <code>ComponentReference</code> and
+ * use the <code>getComponent()</code> to look up the referenced <code>UIComponent</code>.
+ *  
+ * For example, a current weather application might have have a session scoped weatehrBean
+ * containing the current list of locations to report the weather on and support using a
+ * selectMany component to remove the locations:
+ * 
+ * <pre>
+ * <tr:selectManyCheckbox label="Locations" id="smc1" valuePassThru="true"
+ *                        binding="#{weatherBean.locationsSelectManyComponent}"
+ *                       value="#{weatherBean.locationsToRemove}">
+ *    <f:selectItems value="#{weatherBean.locationSelectItems}" id="si1"/>
+ * </tr:selectManyCheckbox>
+ * <tr:commandButton id="deleteCB" text="Remove Locations"
+ *                  actionListener="#{weatherBean.removeLocationListener}">
+ * </tr:commandButton>
+ * </pre>
+ * The weatherBean might looks like this:
+ * <pre>
+ * public class WeatherBean implements Serializable
+ * {
+ *   public void setLocationsToRemove(UIXSelectMany locationsToRemove)
+ *   {
+ *     _locationsToRemove = locationsToRemove;
+ *   }
+ *   
+ *   public UIXSelectMany getLocationsToRemove()
+ *   {
+ *     return _locationsToRemove;
+ *   }
+ *   
+ *   public void removeLocationListener(ActionEvent actionEvent)
+ *   {
+ *     ... code calling getLocationsToRemove() to get the UIXSelectMany ...
+ *   }
+ *
+ *   private UIXSelectMany _locationsToRemove
+ * }
+ * </pre>
+ * This code has several problems:
+ * <ol>
+ *   <li>Since UIComponents aren't Serializable, the class will fail serialization during fail-over
+ *   when default Serialization attempts to serialize _locationsToRemove.</li>
+ *   <li>If the user opens two windows on this page, only the last window rendered have the
+ *   correct UIXSelectMany instance.  If the remove locations button is pressed on the first
+ *   window after rendering the second window, the wrong UIXSelectMany instance will be used.</li>
+ *   <li>Since UIComponents aren't thread-safe, the above case could also result in bizare
+ *   behavior if requests from both windows were being processed by the application server at the
+ *   same time.</li>
+ *   <li>If the Trinidad view state token cache isn't used, or if the user navigates to this page
+ *   using the backbutton, a new UIXSelectMany instance will be created, which also won't match
+ *   the instance we are holding onto.</li>
+ *   <li>If we don't clear the UIXSelectMany instance when we navigate off of this page, we will
+ *   continue to pin the page's UIComponent tree in memory for the lifetime of the Session.
+ *   </li>
+ * </ol>
+ * Rewritten using ComponentReference, the weatherBean might looks like this:
+ * <pre>
+ * public class WeatherBean implements Serializable
+ * {
+ *   public void setLocationsToRemove(UIXSelectMany locationsToRemove)
+ *   {
+ *     _locationsToRemoveRef = UIComponentReference.newUIComponentReference(locationsToRemove);
+ *   }
+ *   
+ *   public UIXSelectMany getLocationsToRemove()
+ *   {
+ *     return _locationsToRemoveRef.getComponent();
+ *   }
+ *   
+ *   public void removeLocationListener(ActionEvent actionEvent)
+ *   {
+ *     ... code calling getLocationsToRemove() to get the UIXSelectMany ...
+ *   }
+ *
+ *   private UIComponentReference<UIXSelectMany> _locationsToRemoveRef
+ * }
+ * </pre>
+ * The above code saves a reference to the component passed to the managed bean and then
+ * retrieves the correct instance given the current UIViewRoot for this request whenever
+ * <code>getLocationsToRemove()</code> is called.
+ * <p><b>Please note:</b>
+ * <ul>
+ * <li>This class is <b>not completely</b> thread-safe, since it depends on <code>UIComponent</code>
+ * APIs, however the class is safe to use as long as either of the following is true
+ * <ol>
+ * <li>The component passed to <code>newUIComponentReference</code> has an id and is in the
+ * component hierarchy when newUIComponentReference is called and all subsequent calls to
+ * <code>getComponent</code> are made from Threads with a valid FacesContext
+ * </li>
+ * <li>The first call to <code>getComponent</code> is on the same Thread that
+ * <code>newUIComponentReference</code> was called on</li>
+ * </ol>
+ * </li>
+ * <li>The passed in <code>UIComponent</code> is <b>required</b> to have an <code>ID</code></li>
+ * <li>The reference will break if the <code>UIComponent</code> is moved between
+ * <code>NamingContainer</code>s <b>or</b>
+ * if any of the ancestor <code>NamingContainer</code>s have their IDs changed.</li>
+ * <li>The reference is persistable. <b>However</b> <code>UIComponent</code>s are not
+ * <code>Serializable</code> and therefore can not be used at any scope longer than request.</li>
+ * </ul>
+ * 
+ * @see ComponentReference#newUIComponentReference(UIComponent)
+ * @see ComponentReference#getComponent()
+ */
+public abstract class ComponentReference<T extends UIComponent> implements Serializable
+{
+  // don't allow other subclasses
+  private ComponentReference(List<Object> componentPath)
+  {
+    _componentPath = componentPath;
+  }
+
+  /**
+   * Factory method to create an instance of the <code>ComponentReference</code> class, which
+   * returns a Serializable and often thread-safe reference to a 
+   * <code>UIComponent</code>.
+   * 
+   * @param component the <code>UIComponent</code> to create a reference to.
+   * @return <code>ComponentReference</code> the reference to the component
+   * @throws NullPointerException if component is <code>null</code>
+   */
+  public static <T extends UIComponent> ComponentReference<T> newUIComponentReference(T component)
+  {
+    // store the id of the component as a transient field since we can grab it from the scoped id
+    // but want it available to validate the component we found from the path
+    String compId = component.getId();
+
+    // if the component is in the hierarchy, the topmost component will be the UIViewRoot
+    if ((compId != null) && (getUIViewRoot(component) != null))
+    {
+      // component has an id and is in the hierarachy, so we can use a stable reference
+      String scopedId = calculateScopedId(component, compId);
+      
+      return new StableComponentReference(scopedId, compId, calculateComponentPath(component));
+    }
+    else
+    {
+      // Oh well, deferred reference it is
+      ComponentReference<T> reference = new DeferredComponentReference<T>(component);
+      
+      // Add to the list of Referernces that may need initialization
+      _addToEnsureInitializationList(reference);
+      
+      return reference;
+    }
+  }
+  
+  /**
+   * This method will use a calculated "component path" to walk down to the <code>UIComponent</code>
+   * that is referenced by this class. If the component can not be found, the <code>getComponent()</code>
+   * will return <code>null</code>. 
+   * 
+   * @return the referenced <code>UIComponent</code> or <code>null</code> if it can not be found.
+   * @throws IllegalStateException if the component used to create the
+   * ComponentReference is not in the component tree or does <b>not</b> have an <code>Id</code>
+   * @see ComponentReference#newUIComponentReference(UIComponent)
+   */
+   @SuppressWarnings("unchecked")
+   public final T getComponent()
+   {
+     // get the scopedId, calculating it if necessary
+     String scopedId = getScopedId();
+         
+     UIComponent foundComponent = null;
+
+     // In order to find the component with its
+     // calculated path, we need to start at the ViewRoot;
+     UIViewRoot root = FacesContext.getCurrentInstance().getViewRoot();
+
+     List<Object> componentPath = _componentPath;
+     
+     if (componentPath != null) 
+     {
+       // Walk down the component tree, to the component we are looking for.
+       // We start at the ViewRoot and use the previous calculated "component path"
+       foundComponent = _walkPathToComponent(root, componentPath);
+     }
+
+     // Check if we really found it with the previously created "component path"
+     if (foundComponent == null || (!getComponentId().equals(foundComponent.getId())))
+     {
+       // OK, we were not luck with the calculated "component path", let's
+       // see if we can find it by using the "scoped ID" and the regular 
+       // findComponent();
+       foundComponent = root.findComponent(scopedId);
+
+       // was the regular findComponent() successful ?
+       if (foundComponent != null)
+       {        
+         // OK, now let's rebuild the path
+         _componentPath = calculateComponentPath(foundComponent);
+       }
+     }
+
+     return (T)foundComponent;
+   }
+
+   /**
+    * Called by the framework to ensure that deferred ComponentReferences are completely
+    * initialized before the UIComponent that the ComponentReference is associated with
+    * is not longer valid.
+    * @throws IllegalStateException if ComponentReference isn't already initialized and 
+    * the component used to create the
+    * ComponentReference is not in the component tree or does <b>not</b> have an <code>Id</code>
+    */
+   public abstract void ensureInitialization();
+    
+  /**
+   * ComponentRefs are required to test for equivalence by the equivalence of their scoped ids
+   * @param o
+   * @return
+   */
+  @Override
+  public final boolean equals(Object o)
+  {
+    if (o == this)
+    {
+      return true;
+    }
+    else if (o instanceof ComponentReference)
+    {
+      return getScopedId().equals(((ComponentReference)o).getScopedId());
+    }
+    else
+    {
+      return false;
+    }
+  }
+ 
+   /**
+    * ComponentRefs must use the hash code of their scoped id as their hash code
+    * @return
+    */
+  @Override
+  public final int hashCode()
+  {
+    return getScopedId().hashCode();
+  }
+  
+  @Override
+  public final String toString()
+  {
+    return super.toString() + ":" + getScopedId();
+  }
+  
+  /**
+   * Returns the scoped id for this ComponentReference
+   * @return
+   */
+  protected abstract String getScopedId();
+
+  /**
+   * Returns the id of the Component that this ComponentReference points to
+   * @return
+   */
+  protected abstract String getComponentId();
+
+  protected final void setComponentPath(List<Object> componentPath)
+  {
+    _componentPath = componentPath;
+  }
+
+  /**
+   * Creates the "component path" started by the given <code>UIComponent</code> up the <code>UIViewRoot</code>
+   * of the underlying component tree. The hierarchy is stored in a <code>List</code> of <code>Object</code>s
+   * (the <code>componentHierarchyList</code> parameter). If the given <code>UIComponent</code> is nested in a 
+   * <code>Facet</code> of a <code>UIComponent</code>, we store the name of the actual <code>facet</code> in
+   * the list. If it is a regular child, we store its position/index. 
+   * 
+   * <p> 
+   * To calculate the <code>scopedID</code> we add the ID of every <code>NamingContainer</code> that we hit,
+   * while walking up the component tree, to the <code>scopedIdList</code>. 
+   * 
+   * @param component The <code>UIComponent</code> for this current iteration
+   * 
+   * @see #newUIComponentReference
+   */
+  protected static List<Object> calculateComponentPath(UIComponent component)
+  {
+    // setUp of list that stores information about the FACET name or the COMPONENT index
+    List<Object> componentHierarchyList = new ArrayList<Object>();
+
+    // stash the component and parent , for the loop
+    UIComponent currComponent = component;
+    UIComponent currParent = currComponent.getParent();
+
+    // enter the loop, if there is a parent for the current component
+    while(currParent != null)
+    {
+      int childIndex = currParent.getChildren().indexOf(currComponent);
+      
+      // is the given component a child of the parent?
+      if (childIndex != -1)
+      {          
+        // if so, add the INDEX (type: int) at the beginning of the list
+        componentHierarchyList.add(childIndex);
+      }
+      else
+      {
+        // If the component is not a child, it must be a facet.
+        // When the component is nested in a facet, we need to find
+        // the name of the embedding FACET
+        Set<Map.Entry<String, UIComponent>> entries = currParent.getFacets().entrySet();
+        for(Map.Entry<String, UIComponent> entry : entries)
+        {
+          if (currComponent.equals(entry.getValue()))
+          {
+            // once we identified the actual component/facet,
+            // we store the name (type: String)at the
+            // beginning of the list and quite the loop afterwards
+            componentHierarchyList.add(entry.getKey());
+            break;
+          }
+        }
+      }
+
+      // set references for the next round of the loop
+      currComponent = currParent;
+      currParent = currParent.getParent();
+    }
+
+    // done with the loop as >currComponent< has no own parent. Which
+    // means we must talk to <code>UIViewRoot</code> here.
+    // Otherwise the component is not connected to the tree, but we should have already checked this
+    // before calling this function
+    if (!(currComponent instanceof UIViewRoot))
+      throw new IllegalStateException(
+                   "The component " + component + " is NOT connected to the component tree");
+  
+    return componentHierarchyList;
+  }
+  
+  protected static String calculateScopedId(
+    UIComponent component,
+    String      componentId)
+  {
+    if (componentId == null)
+      throw new IllegalStateException("Can't create a ComponentReference for component " +
+                                      component +
+                                      " no id");    
+    int scopedIdLength = componentId.length();
+  
+    List<String> scopedIdList = new ArrayList<String>();
+   
+    // determine how many characters we need to store the scopedId.  We skip the component itself,
+    // because we have already accounted for its id
+    UIComponent currAncestor = component.getParent();
+    
+    while (currAncestor != null)
+    {
+      // add the sizes of all of the NamingContainer ancestors, plus 1 for each NamingContainer separator
+      if (currAncestor instanceof NamingContainer)
+      {
+        String currId = currAncestor.getId();
+        scopedIdLength += currId.length() + 1;
+      
+        // add the NamingContainer to the list of NamingContainers
+        scopedIdList.add(currId);
+      }
+      
+      currAncestor = currAncestor.getParent();
+    }
+    
+    // now append all of the NamingContaintes
+    return _createScopedId(scopedIdLength, scopedIdList, componentId);
+  }
+
+  protected Object writeReplace() throws ObjectStreamException
+  {
+    // Only use the proxy when Serializing
+    return new SerializationProxy(getScopedId());
+  }
+  
+  private void readObject(@SuppressWarnings("unused") ObjectInputStream stream) throws InvalidObjectException
+  {
+    // We can't be deserialized directly
+    throw new InvalidObjectException("Proxy required");
+  } 
+
+  /**
+   * Add a reference to the list of References that may need initialization later
+   * @param reference
+   */
+  private static void _addToEnsureInitializationList(ComponentReference<?> reference)
+  {
+    Map<String, Object> requestMap = 
+                            FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
+    
+    Collection<ComponentReference<?>> initializeList = (Collection<ComponentReference<?>>)
+                                             requestMap.get(_FINISH_INITIALIZATION_LIST_KEY);
+    
+    if (initializeList == null)
+    {
+      initializeList = new ArrayList<ComponentReference<?>>();
+      requestMap.put(_FINISH_INITIALIZATION_LIST_KEY, initializeList);
+    }
+    
+    initializeList.add(reference);
+  }
+
+  /**
+   * Transform the <code>scopedIdList</code> of "important" component IDs to 
+   * generate the <code>scopedID</code> for the referenced <code>UIComponent</code>.
+   * 
+   * Uses the <code>scopedIdLength</code>
+   */
+  private static String _createScopedId(int scopedIdLength, List<String> scopedIdList, String componentId)
+  {
+    StringBuilder builder = new StringBuilder(scopedIdLength);
+
+    for (int i = scopedIdList.size() - 1; i >= 0 ; i--)
+    {
+      builder.append(scopedIdList.get(i));
+      builder.append(NamingContainer.SEPARATOR_CHAR);
+    }
+
+    builder.append(componentId);
+    
+    // store the (final) scopedId
+    return builder.toString();
+  }
+  
+  protected static UIViewRoot getUIViewRoot(UIComponent component)
+  {
+    // stash the component and parent , for the loop
+    UIComponent currComponent = component;
+    UIComponent currParent = currComponent.getParent();
+
+    while(currParent != null)
+    {
+      currComponent = currParent;
+      currParent = currParent.getParent();
+    }
+    
+    return (currComponent instanceof UIViewRoot) ? (UIViewRoot)currComponent : null;
+  }
+
+  /**
+   * Starts to walk down the component tree by the given <code>UIViewRoot</code>. It
+   * uses the <code>hierarchyInformationList</code> to check if the it needs to
+   * walk into a FACET or an INDEX of the component.
+   * 
+   * @see ComponentReference#calculateComponentPath(UIComponent)
+   */
+  private UIComponent _walkPathToComponent(UIViewRoot root, List<Object> componentPath)
+  {
+    UIComponent currFound = root;
+
+    // iterate backwards since we appending the items starting from the component
+    for (int i = componentPath.size() - 1; i >= 0 ; i--)
+    {
+      Object location = componentPath.get(i);
+
+      // integer means we need to get the kid at INDEX obj
+      // but let's not try to lookup from a component with
+      // no kids
+      if (location instanceof Integer)
+      {
+        int childIndex = ((Integer)location).intValue();
+        
+        List<UIComponent> children = currFound.getChildren();
+
+        // make sure there is actually a child at this index
+        if (childIndex < children.size())
+        {
+          currFound = children.get(childIndex);
+        }
+        else
+        {
+          // something changed, there aren't enough children so give up
+          return null;
+        }
+      }
+      else
+      {
+        // there is only ONE child per facet! So get the
+        // component of FACET "obj"
+        String facetName = location.toString();
+
+        currFound = currFound.getFacets().get(facetName);
+
+        // component isn't under the same facet anymore, so give up
+        if (currFound == null)
+          return null;
+      }
+    }
+    return currFound;
+  }
+  
+  /**
+   * ComponentReference where the scopedId is calculatable at creation time
+   */
+  private static final class StableComponentReference extends ComponentReference
+  {
+    private StableComponentReference(String scopedId)
+    {      
+      this(scopedId,
+           // String.substring() is optimized to return this if the entire string
+           // is the substring, so no further optimization is necessary
+           scopedId.substring(scopedId.lastIndexOf(NamingContainer.SEPARATOR_CHAR)+1),
+           null);
+    }
+ 
+    private StableComponentReference(
+      String scopedId,
+      String componentId,
+      List<Object> componentPath)
+    {
+      super(componentPath);
+
+      if (scopedId == null)
+        throw new NullPointerException();
+      
+      _scopedId = scopedId;
+      _componentId = componentId;
+    }
+    
+    public void ensureInitialization()
+    {
+      // do nothing--stable references are always fully initialized
+    }
+
+    protected String getScopedId()
+    {
+      return _scopedId;
+    }
+
+    protected String getComponentId()
+    {
+      return _componentId;
+    }
+ 
+    private final String _componentId;
+    private final String _scopedId;
+
+    private static final long serialVersionUID = 1L;
+  }
+  
+  /**
+   * ComponentReference where the component isn't ready to have its ComponentReference calculated at
+   * creation time.  Instead we wait until getComponent() is called, or the ComponentReference is
+   * Serialized.
+   */
+  private static final class DeferredComponentReference<T extends UIComponent> extends ComponentReference
+  {
+    /**
+     * Private constructor, used by <code>ComponentReference.newUIComponentReference</code> 
+     * @param component the <code>UIComponent</code> we want to store the path for
+     */
+    private DeferredComponentReference(T component)
+    {
+      super(null);
+      
+      // temporarily store away the component
+      _component = component;
+    }
+
+    public void ensureInitialization()
+    {
+      // getScopedId() ensures we are initialized
+      getScopedId();
+    }
+
+    protected String getScopedId()
+    {
+      String scopedId = _scopedId;
+      
+      // we have no scopedId, so calculate the scopedId and finish initializing
+      // the DeferredComponentReference
+      if (scopedId == null)
+      {
+        UIComponent component = _component;
+        
+        // need to check that component isn't null because of possible race condition if this
+        // method is called from different threads.  In that case, scopedId will have been filled
+        // in, so we can return it
+        if (component != null)
+        {
+          String componentId = component.getId();
+                    
+          scopedId = calculateScopedId(component, componentId);
+          _scopedId = scopedId;
+          _componentId = componentId;
+          
+          // store away our component path while we can efficiently calculate it
+          setComponentPath(calculateComponentPath(component));
+          
+          _component = null;
+        }
+        else
+        {
+          scopedId = _scopedId;
+        }
+      }
+      
+      return scopedId;
+    }
+
+    protected String getComponentId()
+    {
+      return _componentId;
+    }
+    
+    private transient T _component;
+    private transient volatile String _componentId;
+    private volatile String _scopedId;
+
+    private static final long serialVersionUID = 1L;
+  }
+
+  /**
+   * Proxy class for serializing ComponentReferences.  The Serialized for is simply the scopedId
+   */
+  private static final class SerializationProxy implements Serializable
+  {
+    SerializationProxy(String scopedId)
+    {
+      _scopedId = scopedId;
+    }
+
+    private Object readResolve()
+    {
+      return new StableComponentReference(_scopedId);
+    }
+      
+    private final String _scopedId;
+
+    private static final long serialVersionUID = 1L;
+  }
+
+  private transient volatile List<Object> _componentPath;
+
+  private static final String _FINISH_INITIALIZATION_LIST_KEY = ComponentReference.class.getName() +
+                                                                "#FINISH_INITIALIZATION";
+  
+  private static final long serialVersionUID = -6803949693688638969L;
+}
\ No newline at end of file

Added: myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-api/src/test/java/org/apache/myfaces/trinidad/util/ComponentReferenceTest.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-api/src/test/java/org/apache/myfaces/trinidad/util/ComponentReferenceTest.java?rev=920601&view=auto
==============================================================================
--- myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-api/src/test/java/org/apache/myfaces/trinidad/util/ComponentReferenceTest.java (added)
+++ myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-api/src/test/java/org/apache/myfaces/trinidad/util/ComponentReferenceTest.java Tue Mar  9 01:02:35 2010
@@ -0,0 +1,545 @@
+/*
+ *  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.trinidad.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import javax.faces.component.UIData;
+import javax.faces.component.UIForm;
+import javax.faces.component.UIInput;
+import javax.faces.component.UINamingContainer;
+import javax.faces.component.UIOutput;
+import javax.faces.component.UIPanel;
+import javax.faces.component.UIViewRoot;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.myfaces.trinidadbuild.test.FacesTestCase;
+
+public class ComponentReferenceTest extends FacesTestCase
+{
+  public static final Test suite()
+  {
+    return new TestSuite(ComponentReferenceTest.class);
+  }
+  
+  public static void main(String[] args) throws Throwable
+  {
+    junit.textui.TestRunner.run(suite());
+  }
+
+  public ComponentReferenceTest(
+    String testName)
+  {
+    super(testName);
+  }
+  
+  public void testVerySimpleFinder()
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UIInput input1 = new UIInput(); input1.setId("input1");
+
+    // build the Tree...
+    root.getChildren().add(input1);
+
+    // Get the ComponentReference util
+    ComponentReference<UIInput> uiRef = ComponentReference.newUIComponentReference(input1);
+
+    // find the component...
+    UIInput referencedComp = uiRef.getComponent();
+
+    assertEquals(input1, referencedComp);
+  }
+
+  @SuppressWarnings("unchecked")
+  public void testFailoverOnCustomFacet() throws IOException, ClassNotFoundException
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UIForm form = new UIForm(); form.setId("form");
+    UINamingContainer nc1 = new UINamingContainer(); nc1.setId("nc1");
+    UINamingContainer nc2 = new UINamingContainer(); nc2.setId("nc2");
+    UINamingContainer nc3 = new UINamingContainer(); nc3.setId("nc3");
+    UIPanel panel = new UIPanel(); panel.setId("panel");
+    UIInput input = new UIInput(); input.setId("input1");
+
+    // build the Tree...
+    panel.getFacets().put("fancyFacet", input);
+    nc3.getChildren().add(panel);
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(nc3);
+    nc1.getChildren().add(nc2);
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(nc1);
+    root.getChildren().add(new UIOutput());
+    root.getChildren().add(form);
+
+    // Get the ComponentReference util
+    ComponentReference<UIInput> uiRef = ComponentReference.newUIComponentReference(input);
+
+    // find the component...
+    UIInput referencedComp = uiRef.getComponent();
+    assertEquals(input, referencedComp);
+    
+    // find it again!
+    assertEquals(input, uiRef.getComponent());
+
+    // fake the failover
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    ObjectOutputStream oos = new ObjectOutputStream(baos);
+
+    oos.writeObject(uiRef);
+
+    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+    ObjectInputStream ois = new ObjectInputStream(bais);
+
+    uiRef = (ComponentReference<UIInput>) ois.readObject();
+
+    referencedComp = uiRef.getComponent();
+    assertEquals(input, referencedComp);
+
+  }
+
+  
+  @SuppressWarnings("unchecked")
+  public void testFailover() throws IOException, ClassNotFoundException
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UIInput input1 = new UIInput(); input1.setId("input1");
+
+    // build the Tree...
+    root.getChildren().add(input1);
+
+    // Get the ComponentReference util
+    ComponentReference<UIInput> uiRef = ComponentReference.newUIComponentReference(input1);
+
+    // find the component...
+    UIInput referencedComp = uiRef.getComponent();
+    assertEquals(input1, referencedComp);
+    
+    // fake the failover
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    ObjectOutputStream oos = new ObjectOutputStream(baos);
+
+    oos.writeObject(uiRef);
+    oos.flush();
+
+    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+    ObjectInputStream ois = new ObjectInputStream(bais);
+
+    uiRef = (ComponentReference<UIInput>) ois.readObject();
+
+    referencedComp = uiRef.getComponent();
+    assertEquals(input1, referencedComp);
+  }
+
+  public void testEmptyViewRootOnGetComponent()
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UINamingContainer nc1 = new UINamingContainer(); nc1.setId("nc1");
+    UINamingContainer nc2 = new UINamingContainer(); nc2.setId("nc2");
+    UINamingContainer nc3 = new UINamingContainer(); nc3.setId("nc3");
+
+    // build the tree
+    root.getChildren().add(nc1);
+    nc1.getChildren().add(nc2);
+    nc2.getChildren().add(nc3);
+
+    // Get the ComponentReference util
+    ComponentReference<UINamingContainer> uiRef = ComponentReference.newUIComponentReference(nc3);
+
+    // find the component...
+    UINamingContainer referencedComp = uiRef.getComponent();
+
+    assertEquals(nc3, referencedComp);
+
+    // clear the ViewRoot
+    root.getChildren().clear();
+
+    // now, the getComponent should return NULL
+    assertNull(uiRef.getComponent());
+  }
+
+  public void testNoComponentWithoutAnId()
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UIInput input1 = new UIInput();
+
+    // build the Tree...
+    root.getChildren().add(input1);
+
+    ComponentReference ref = ComponentReference.newUIComponentReference(input1);
+
+    // Get the ComponentReference util
+    try
+    {
+      ref.getComponent();
+      
+      // find the component...
+      fail("IllegalStateException expected");
+    }
+    catch (IllegalStateException e)
+    {
+      // suppress it - this is as expected
+    }
+  }
+
+  public void testViewRoot()
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+
+    // Get the ComponentReference util
+    ComponentReference<UIViewRoot> uiRef = ComponentReference.newUIComponentReference(root);
+
+    // find the component...
+    UIViewRoot referencedComp = uiRef.getComponent();
+    assertEquals(root, referencedComp);
+  }
+
+  public void testNamingContainerViewRoot()
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UINamingContainer nc1 = new UINamingContainer(); nc1.setId("nc1");
+    UINamingContainer nc2 = new UINamingContainer(); nc2.setId("nc2");
+    UINamingContainer nc3 = new UINamingContainer(); nc3.setId("nc3");
+
+    // build the tree
+    root.getChildren().add(nc1);
+    nc1.getChildren().add(nc2);
+    nc2.getChildren().add(nc3);
+
+    // Get the ComponentReference util
+    ComponentReference<UINamingContainer> uiRef = ComponentReference.newUIComponentReference(nc3);
+
+    // find the component...
+    UINamingContainer referencedComp = uiRef.getComponent();
+
+    assertEquals(nc3, referencedComp);
+  }
+
+  public void testMovingInsideNamingContainer()
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UINamingContainer nc1 = new UINamingContainer(); nc1.setId("nc1");
+    UINamingContainer nc2 = new UINamingContainer(); nc2.setId("nc2");
+    UINamingContainer nc3 = new UINamingContainer(); nc3.setId("nc3");
+
+    // build the tree
+    root.getChildren().add(nc1);
+    nc1.getChildren().add(nc2);
+    nc2.getChildren().add(nc3);
+
+    // Get the ComponentReference util
+    ComponentReference<UINamingContainer> uiRef = ComponentReference.newUIComponentReference(nc3);
+
+    // find the component...
+    UINamingContainer referencedComp = uiRef.getComponent();
+    assertEquals(nc3, referencedComp);
+
+    // let's move the NC3 component one level up;
+    nc2.getChildren().remove(nc3);
+    nc1.getChildren().add(nc3);
+
+    // and we can not find the component...
+    referencedComp = uiRef.getComponent();
+    assertNull(referencedComp);
+  }
+
+  public void testDeferredMovingInsideNamingContainer()
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UINamingContainer nc1 = new UINamingContainer(); nc1.setId("nc1");
+    UINamingContainer nc2 = new UINamingContainer(); nc2.setId("nc2");
+    UINamingContainer nc3 = new UINamingContainer(); nc3.setId("nc3");
+
+    // almost build the tree
+    nc1.getChildren().add(nc2);
+    nc2.getChildren().add(nc3);
+
+    // Get the ComponentReference util, this will be a deferred component reference since the
+    // component wasn't attached
+    ComponentReference<UINamingContainer> uiRef = ComponentReference.newUIComponentReference(nc3);
+
+    // now finish building the component tree
+    root.getChildren().add(nc1);
+
+    // find the component...
+    UINamingContainer referencedComp = uiRef.getComponent();
+    assertEquals(nc3, referencedComp);
+
+    // let's move the NC3 component one level up;
+    nc2.getChildren().remove(nc3);
+    nc1.getChildren().add(nc3);
+
+    // and we can not find the component...
+    referencedComp = uiRef.getComponent();
+    assertNull(referencedComp);
+  }
+
+  public void testComponentNotInTree()
+  {
+    UINamingContainer nc1 = new UINamingContainer(); nc1.setId("nc1");
+    UINamingContainer nc2 = new UINamingContainer(); nc2.setId("nc2");
+    UINamingContainer nc3 = new UINamingContainer(); nc3.setId("nc3");
+
+    // build the tree
+    nc1.getChildren().add(nc2);
+    nc2.getChildren().add(nc3);
+
+    // Get the ComponentReference util
+    ComponentReference ref = ComponentReference.newUIComponentReference(nc3);
+
+    try
+    {
+      // find the component...
+      ref.getComponent();
+      
+      fail("IllegalStateException expected");
+    }
+    catch (IllegalStateException e)
+    {
+      // suppress it - this is as expected
+    }
+  }
+
+  public void testFindInPanelComponents()
+  {
+    UIViewRoot root = facesContext.getViewRoot(); root.setId("root");
+    UIPanel panel1 = new UIPanel(); panel1.setId("panel1");
+    UIPanel panel2 = new UIPanel(); panel2.setId("panel2");
+    UIPanel panel3 = new UIPanel(); panel3.setId("panel3");
+    UIOutput txt = new UIOutput(); txt.setId("txt");
+
+    // build the Tree...
+    root.getChildren().add(panel1);
+    panel1.getChildren().add(panel2);
+    panel2.getChildren().add(panel3);
+    panel3.getChildren().add(txt);
+
+    // Get the ComponentReference util
+    ComponentReference<UIOutput> uiRef = ComponentReference.newUIComponentReference(txt);
+
+    // find the component...
+    UIOutput referencedComp = uiRef.getComponent();
+
+    assertEquals(txt, referencedComp);
+  }
+
+  public void testCustomFacet()
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UIForm form = new UIForm(); form.setId("form");
+    UINamingContainer nc1 = new UINamingContainer(); nc1.setId("nc1");
+    UINamingContainer nc2 = new UINamingContainer(); nc2.setId("nc2");
+    UINamingContainer nc3 = new UINamingContainer(); nc3.setId("nc3");
+    UIPanel panel = new UIPanel(); panel.setId("panel");
+    UIInput input = new UIInput(); input.setId("input1");
+
+    // build the Tree...
+    panel.getFacets().put("fancyFacet", input);
+    nc3.getChildren().add(panel);
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(nc3);
+    nc1.getChildren().add(nc2);
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(nc1);
+    root.getChildren().add(new UIOutput());
+    root.getChildren().add(form);
+
+    // Get the ComponentReference util
+    ComponentReference<UIInput> uiRef = ComponentReference.newUIComponentReference(input);
+
+    // find the component...
+    UIInput referencedComp = uiRef.getComponent();
+    assertEquals(input, referencedComp);
+    
+    // find it again!
+    assertEquals(input, uiRef.getComponent());
+  }
+
+  public void testMoreFacets()
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UIForm form = new UIForm(); form.setId("form");
+    UINamingContainer nc1 = new UINamingContainer(); nc1.setId("nc1");
+    UINamingContainer nc2 = new UINamingContainer(); nc2.setId("nc2");
+    UINamingContainer nc3 = new UINamingContainer(); nc3.setId("nc3");
+    UIPanel panel = new UIPanel(); panel.setId("panel");
+    UIInput input = new UIInput(); input.setId("input1");
+
+    // build the Tree...
+    panel.getFacets().put("f1", new UIOutput());
+    panel.getFacets().put("f2", new UIOutput());
+    panel.getFacets().put("f3", new UIOutput());
+    panel.getFacets().put("f4", new UIOutput());
+    panel.getFacets().put("f5", new UIOutput());
+
+    // add the important facet
+    panel.getFacets().put("f0", input);
+
+    nc3.getChildren().add(panel);
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(nc3);
+    nc1.getChildren().add(nc2);
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(nc1);
+    root.getChildren().add(new UIOutput());
+    root.getChildren().add(form);
+
+    // Get the ComponentReference util
+    ComponentReference<UIInput> uiRef = ComponentReference.newUIComponentReference(input);
+
+    // find the component...
+    UIInput referencedComp = uiRef.getComponent();
+    assertEquals(input, referencedComp);
+    
+    // find it again!
+    assertEquals(input, uiRef.getComponent());
+  }
+
+  public void testCustomFacetWithFind()
+  {
+    UIViewRoot root = new UIViewRoot();
+    root.setId("root");
+    UIForm form = new UIForm(); form.setId("form");
+    UINamingContainer nc1 = new UINamingContainer(); nc1.setId("nc1");
+    UINamingContainer nc2 = new UINamingContainer(); nc2.setId("nc2");
+    UINamingContainer nc3 = new UINamingContainer(); nc3.setId("nc3");
+    UIPanel panel = new UIPanel(); panel.setId("panel");
+    UIInput input = new UIInput(); input.setId("input1");
+
+    // build the Tree...
+    panel.getFacets().put("fancyFacet", input);
+    panel.getChildren().add(input);
+    nc3.getChildren().add(panel);
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(nc3);
+    nc1.getChildren().add(nc2);
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(nc1);
+    root.getChildren().add(new UIOutput());
+    root.getChildren().add(form);
+    
+    facesContext.setViewRoot(root);
+
+    // Get the ComponentReference util
+    ComponentReference<UIInput> uiRef = ComponentReference.newUIComponentReference(input);
+
+    // find the component...
+    UIInput referencedComp = uiRef.getComponent();
+    assertEquals(input, referencedComp);
+
+    // find it again!
+    assertEquals(input, uiRef.getComponent());
+
+  }
+  
+  public void testUIDataFooterFacet()
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UIForm form = new UIForm(); form.setId("form");
+    UINamingContainer nc1 = new UINamingContainer(); nc1.setId("nc1");
+    UINamingContainer nc2 = new UINamingContainer(); nc2.setId("nc2");
+    UINamingContainer nc3 = new UINamingContainer(); nc3.setId("nc3");
+    UIData table = new UIData(); table.setId("table1");
+    UIInput input = new UIInput(); input.setId("input1");
+
+    // build the Tree...
+    table.setFooter(input);
+    nc3.getChildren().add(table);
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(nc3);
+    nc1.getChildren().add(nc2);
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(nc1);
+    root.getChildren().add(new UIOutput());
+    root.getChildren().add(form);
+
+    // Get the ComponentReference util
+    ComponentReference<UIInput> uiRef = ComponentReference.newUIComponentReference(input);
+
+    // find the component...
+    UIInput referencedComp = uiRef.getComponent();
+
+    assertEquals(input, referencedComp);
+  }
+
+  public void testIndex()
+  {
+    UIViewRoot root = facesContext.getViewRoot();
+    root.setId("root");
+    UIForm form = new UIForm(); form.setId("form");
+    UINamingContainer nc1 = new UINamingContainer(); nc1.setId("nc1");
+    UINamingContainer nc2 = new UINamingContainer(); nc2.setId("nc2");
+    UINamingContainer nc3 = new UINamingContainer(); nc3.setId("nc3");
+    UIInput input1 = new UIInput(); input1.setId("input1");
+
+    // build the Tree...
+    nc3.getChildren().add(input1);
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(new UIOutput());
+    nc2.getChildren().add(nc3);
+    nc1.getChildren().add(nc2);
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(new UIOutput());
+    form.getChildren().add(nc1);
+    root.getChildren().add(new UIOutput());
+    root.getChildren().add(form);
+
+    // Get the ComponentReference util
+    ComponentReference<UIInput> uiRef = ComponentReference.newUIComponentReference(input1);
+
+    // find the component...
+    UIInput referencedComp = uiRef.getComponent();
+
+    assertEquals(input1, referencedComp);
+  }
+}
\ No newline at end of file

Modified: myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java?rev=920601&r1=920600&r2=920601&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java (original)
+++ myfaces/trinidad/branches/1.2.12.1.2-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java Tue Mar  9 01:02:35 2010
@@ -19,6 +19,8 @@
 
 package org.apache.myfaces.trinidadinternal.config;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -27,6 +29,8 @@
 
 import javax.faces.context.ExternalContext;
 
+import javax.faces.context.FacesContext;
+
 import javax.servlet.ServletRequest;
 
 import javax.servlet.ServletRequestWrapper;
@@ -40,6 +44,7 @@
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 import org.apache.myfaces.trinidad.skin.SkinFactory;
 import org.apache.myfaces.trinidad.util.ClassLoaderUtils;
+import org.apache.myfaces.trinidad.util.ComponentReference;
 import org.apache.myfaces.trinidadinternal.context.RequestContextFactoryImpl;
 import org.apache.myfaces.trinidadinternal.context.external.ServletCookieMap;
 import org.apache.myfaces.trinidadinternal.context.external.ServletRequestHeaderMap;
@@ -409,7 +414,6 @@
       }
       finally
       {
-
         //Do cleanup of anything which may have use the thread local manager durring
         //init.
         _releaseManagedThreadLocals();
@@ -480,12 +484,19 @@
     RequestContext context = RequestContext.getCurrentInstance();
     if (context != null)
     {
+      // ensure that any deferred ComponentReferences are initialized
+      _finishComponentReferenceInitialization(ec);
+
       context.release();
       _releaseManagedThreadLocals();
+      
       assert RequestContext.getCurrentInstance() == null;
     }
   }
 
+  /**
+   * Ensure that any ThreadLocals initialized during this request are cleared
+   */
   private void _releaseManagedThreadLocals()
   {
     ThreadLocalResetter resetter = _threadResetter.get();
@@ -495,6 +506,29 @@
       resetter.__removeThreadLocals();
     }
   }
+  
+  /**
+   * Ensure that all DeferredComponentReferences are fully initialized before the
+   * request completes
+   */
+  private void _finishComponentReferenceInitialization(ExternalContext ec)
+  {
+    Map<String, Object> requestMap = ec.getRequestMap();
+    
+    Collection<ComponentReference<?>> initializeList = (Collection<ComponentReference<?>>)
+                                             requestMap.get(_FINISH_INITIALIZATION_LIST_KEY);
+    
+    if ((initializeList != null) && !initializeList.isEmpty())
+    {
+      for (ComponentReference<?> reference : initializeList)
+      {
+        reference.ensureInitialization();
+      }
+      
+      // we've initialized everything, so we're done
+      initializeList.clear();
+    }
+  }
 
   private void _endConfiguratorServiceRequest(final ExternalContext ec)
   {
@@ -738,6 +772,10 @@
     static private String _TEST_PARAM = TestRequest.class.getName() + ".TEST_PARAM";
   }
 
+  // skanky duplication of key from ComponentReference Class
+  private static final String _FINISH_INITIALIZATION_LIST_KEY = ComponentReference.class.getName() +
+                                                                "#FINISH_INITIALIZATION";
+
   // hacky reference to the ThreadLocalResetter used to clean up request-scoped
   // ThreadLocals
   private AtomicReference<ThreadLocalResetter> _threadResetter =